Merge "Load VNS property access gatekeeping from file" into nyc-dev
diff --git a/bluetooth/A2dpSinkService/src/com/google/android/car/a2dpsink/A2dpMediaBrowserService.java b/bluetooth/A2dpSinkService/src/com/google/android/car/a2dpsink/A2dpMediaBrowserService.java
index 825af4b..5fc912c 100644
--- a/bluetooth/A2dpSinkService/src/com/google/android/car/a2dpsink/A2dpMediaBrowserService.java
+++ b/bluetooth/A2dpSinkService/src/com/google/android/car/a2dpsink/A2dpMediaBrowserService.java
@@ -281,6 +281,7 @@
Log.d(TAG, "msgDeviceConnect");
// We are connected to a new device via A2DP now.
mA2dpDevice = device;
+ handleProfileOrDeviceConnect();
}
private void msgProfileConnect(BluetoothProfile profile) {
@@ -297,18 +298,28 @@
if (mA2dpDevice != null && !mA2dpDevice.equals(devices.get(0))) {
Log.e(TAG, "A2dp device : " + mA2dpDevice + " avrcp device " + devices.get(0));
+ return;
}
mA2dpDevice = devices.get(0);
+ handleProfileOrDeviceConnect();
+ }
- PlaybackState playbackState = mAvrcpProfile.getPlaybackState(mA2dpDevice);
+ // Called when either the device or profile is connected. Note that either may be connected
+ // independent of each other. We set the initial state only if both are connected.
+ private void handleProfileOrDeviceConnect() {
+ if (mA2dpDevice == null || mAvrcpProfile == null) {
+ Log.e(TAG, "device and profile should be non-null.");
+ return;
+ }
+
+ // Set a dummy playback state so that the user can press play and start music.
// Add actions required for playback and rebuild the object.
- PlaybackState.Builder pbb = new PlaybackState.Builder(playbackState);
- playbackState = pbb.setActions(mTransportControlFlags).build();
-
- MediaMetadata mediaMetadata = mAvrcpProfile.getMetadata(mA2dpDevice);
- Log.d(TAG, "Media metadata " + mediaMetadata + " playback state " + playbackState);
- mSession.setMetadata(mAvrcpProfile.getMetadata(mA2dpDevice));
- mSession.setPlaybackState(playbackState);
+ PlaybackState.Builder pbb = new PlaybackState.Builder();
+ pbb = pbb.setState(PlaybackState.STATE_STOPPED, PlaybackState.PLAYBACK_POSITION_UNKNOWN,
+ PLAYBACK_SPEED)
+ .setActions(mTransportControlFlags);
+ mSession.setPlaybackState(pbb.build());
+ mSession.setMetadata(new MediaMetadata.Builder().build());
}
private void msgDeviceDisconnect(BluetoothDevice device) {
@@ -329,6 +340,7 @@
.setActions(mTransportControlFlags)
.setErrorMessage(getString(R.string.bluetooth_disconnected));
mSession.setPlaybackState(pbb.build());
+ mSession.setMetadata(null);
}
private void msgProfileDisconnect() {
diff --git a/bluetooth/BtPbapSinkService/src/com/google/android/car/pbapsink/PhonebookPullRequest.java b/bluetooth/BtPbapSinkService/src/com/google/android/car/pbapsink/PhonebookPullRequest.java
index b88ceb6..0d7b090 100644
--- a/bluetooth/BtPbapSinkService/src/com/google/android/car/pbapsink/PhonebookPullRequest.java
+++ b/bluetooth/BtPbapSinkService/src/com/google/android/car/pbapsink/PhonebookPullRequest.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.google.android.car.pbapsink;
import com.android.vcard.VCardEntry;
diff --git a/bluetooth/bt-map-client-lib/src/com/google/android/auto/mapservice/BluetoothMapManager.java b/bluetooth/bt-map-client-lib/src/com/google/android/auto/mapservice/BluetoothMapManager.java
index a8c41cc..9bdc215 100644
--- a/bluetooth/bt-map-client-lib/src/com/google/android/auto/mapservice/BluetoothMapManager.java
+++ b/bluetooth/bt-map-client-lib/src/com/google/android/auto/mapservice/BluetoothMapManager.java
@@ -1,405 +1,405 @@
- /*
- * 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.
- */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
- package com.google.android.auto.mapservice;
+package com.google.android.auto.mapservice;
- import android.bluetooth.BluetoothDevice;
- import android.bluetooth.BluetoothAdapter;
- import android.content.BroadcastReceiver;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.content.ServiceConnection;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Looper;
- import android.os.RemoteException;
- import android.util.Log;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
- import java.lang.ref.WeakReference;
- import java.util.List;
+import java.lang.ref.WeakReference;
+import java.util.List;
- public final class BluetoothMapManager {
- public static final String CALLBACK_MISMATCH = "CALLBACK_MISMATCH";
+public final class BluetoothMapManager {
+ public static final String CALLBACK_MISMATCH = "CALLBACK_MISMATCH";
- /**
- * Connection State(s) for the service bound to this Manager.
- * The connection state manage if the Manager is connected to Service:
- * DISCONNECTED: Manager cannot execute calls on service currently because the client either
- * never called connect() on the manager OR the device is disconnected. In the later case the
- * client will have to eventually call connect() again.
- * CONNECTING: This state persists from calling onBind on the service, until we have
- * successfully received onConnect from the service.
- * CONNECTED: The service is successfully connected and ready to receive commands.
- */
- private static final int DISCONNECTED = 0;
- private static final int SUSPENDED = 1;
- private static final int CONNECTING = 2;
- private static final int CONNECTED = 3;
+ /**
+ * Connection State(s) for the service bound to this Manager.
+ * The connection state manage if the Manager is connected to Service:
+ * DISCONNECTED: Manager cannot execute calls on service currently because the client either
+ * never called connect() on the manager OR the device is disconnected. In the later case the
+ * client will have to eventually call connect() again.
+ * CONNECTING: This state persists from calling onBind on the service, until we have
+ * successfully received onConnect from the service.
+ * CONNECTED: The service is successfully connected and ready to receive commands.
+ */
+ private static final int DISCONNECTED = 0;
+ private static final int SUSPENDED = 1;
+ private static final int CONNECTING = 2;
+ private static final int CONNECTED = 3;
- // Error codes returned via the onError call.
+ // Error codes returned via the onError call.
- // On connection suspended the Manager will callback with onConnect when the service is
- // restarted and connected by android binder.
- public static final int ERROR_CONNECT_SUSPENDED = 0;
- // Connection between the service (backed by this Manager) and the remote device has failed.
- // It can be due to variety of reasons such as obex transport failure or the device going out
- // of range. The client will need to either call connect() again. In cases where the device
- // goes out of range, calling connect agian will lead to this error being throw again but if
- // it was a transient failure due to obex transport or other binder issue, then this call will
- // succeed.
- public static final int ERROR_CONNECT_FAILED = 1;
+ // On connection suspended the Manager will callback with onConnect when the service is
+ // restarted and connected by android binder.
+ public static final int ERROR_CONNECT_SUSPENDED = 0;
+ // Connection between the service (backed by this Manager) and the remote device has failed.
+ // It can be due to variety of reasons such as obex transport failure or the device going out
+ // of range. The client will need to either call connect() again. In cases where the device
+ // goes out of range, calling connect agian will lead to this error being throw again but if
+ // it was a transient failure due to obex transport or other binder issue, then this call will
+ // succeed.
+ public static final int ERROR_CONNECT_FAILED = 1;
- // String representation of operations.
- private static final String OP_NONE = "";
- private static final String OP_PUSH_MESSAGE = "pushMessage";
- private static final String OP_GET_MESSAGE = "getMessage";
- private static final String OP_GET_MESSAGES_LISTING = "getMessagesListing";
- private static final String OP_ENABLE_NOTIFICATIONS = "enableNotifications";
+ // String representation of operations.
+ private static final String OP_NONE = "";
+ private static final String OP_PUSH_MESSAGE = "pushMessage";
+ private static final String OP_GET_MESSAGE = "getMessage";
+ private static final String OP_GET_MESSAGES_LISTING = "getMessagesListing";
+ private static final String OP_ENABLE_NOTIFICATIONS = "enableNotifications";
- private static final boolean DBG = true;
- private static final String TAG = "BluetoothMapManager";
- private final Context mContext;
- private final ConnectionCallbacks mCallbacks;
- private final BluetoothDevice mDevice;
- // We have a handler to make sure that all code modifying/accessing the final non-final
- // objects are serialized. This is done by ensuring the following:
- // a) All calls done by the client (using this manager) should be on the main thread.
- // b) All calls done by the manager not-on main thread (binder threads) should be posted back
- // to main thread using this handler.
- private final Handler mHandler = new Handler();
+ private static final boolean DBG = true;
+ private static final String TAG = "BluetoothMapManager";
+ private final Context mContext;
+ private final ConnectionCallbacks mCallbacks;
+ private final BluetoothDevice mDevice;
+ // We have a handler to make sure that all code modifying/accessing the final non-final
+ // objects are serialized. This is done by ensuring the following:
+ // a) All calls done by the client (using this manager) should be on the main thread.
+ // b) All calls done by the manager not-on main thread (binder threads) should be posted back
+ // to main thread using this handler.
+ private final Handler mHandler = new Handler();
- private IBluetoothMapService mServiceBinder;
- private IBluetoothMapServiceCallbacks mServiceCallbacks;
- private BluetoothMapServiceConnection mServiceConnection;
- private int mConnectionState = DISCONNECTED;
- private String mOpInflight = OP_NONE;
+ private IBluetoothMapService mServiceBinder;
+ private IBluetoothMapServiceCallbacks mServiceCallbacks;
+ private BluetoothMapServiceConnection mServiceConnection;
+ private int mConnectionState = DISCONNECTED;
+ private String mOpInflight = OP_NONE;
- public BluetoothMapManager(
- Context context, BluetoothDevice device, ConnectionCallbacks callbacks) {
- if (device == null) {
- throw new IllegalArgumentException("Device cannot be null.");
- }
- if (callbacks == null) {
- throw new IllegalArgumentException(TAG + ": Callbacks cannot be null!");
- }
+ public BluetoothMapManager(
+ Context context, BluetoothDevice device, ConnectionCallbacks callbacks) {
+ if (device == null) {
+ throw new IllegalArgumentException("Device cannot be null.");
+ }
+ if (callbacks == null) {
+ throw new IllegalArgumentException(TAG + ": Callbacks cannot be null!");
+ }
- if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
- throw new IllegalStateException(
- "Client needs to call the manager from the main UI thread.");
- }
+ if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Client needs to call the manager from the main UI thread.");
+ }
- mDevice = device;
- mContext = context;
- mCallbacks = callbacks;
- }
+ mDevice = device;
+ mContext = context;
+ mCallbacks = callbacks;
+ }
- /**
- * Defines the callback interface that clients using the Manager should implement in order to
- * receive callbacks and notification for changes happening to either the binder connection
- * between the Manager and Service or MAP profile changes.
- */
- public static abstract class ConnectionCallbacks {
- /**
- * Called when connection has been established successfully with the service and the
- * service itself is successfully connected to MAP profile.
- * See connect().
- */
- public abstract void onConnected();
+ /**
+ * Defines the callback interface that clients using the Manager should implement in order to
+ * receive callbacks and notification for changes happening to either the binder connection
+ * between the Manager and Service or MAP profile changes.
+ */
+ public static abstract class ConnectionCallbacks {
+ /**
+ * Called when connection has been established successfully with the service and the
+ * service itself is successfully connected to MAP profile.
+ * See connect().
+ */
+ public abstract void onConnected();
- /**
- * Called when the Manager is no longer able to execute commands on the service.
- * Client who holds this manager should consider all in-flight commands sent till now as
- * cancelled. For permanent failures such as device going out of range and not coming back
- * the client should call connect() again too continue working otherwise
- * when the Manager does connect back it will call onConnected() (see above).
- */
- public abstract void onError(int errorCode);
+ /**
+ * Called when the Manager is no longer able to execute commands on the service.
+ * Client who holds this manager should consider all in-flight commands sent till now as
+ * cancelled. For permanent failures such as device going out of range and not coming back
+ * the client should call connect() again too continue working otherwise
+ * when the Manager does connect back it will call onConnected() (see above).
+ */
+ public abstract void onError(int errorCode);
- /**
- * Callen when notification status has been adjusted.
- * See enableNotifications().
- */
- public abstract void onEnableNotifications();
+ /**
+ * Callen when notification status has been adjusted.
+ * See enableNotifications().
+ */
+ public abstract void onEnableNotifications();
- /**
- * Called when the message has been queued for sending.
- * The argument always contains a "valid" handle.
- * See pushMessage().
- */
- public abstract void onPushMessage(String handle);
+ /**
+ * Called when the message has been queued for sending.
+ * The argument always contains a "valid" handle.
+ * See pushMessage().
+ */
+ public abstract void onPushMessage(String handle);
- /**
- * Called when the message is fetched.
- * See getMessage().
- */
- public abstract void onGetMessage(BluetoothMapMessage msg);
+ /**
+ * Called when the message is fetched.
+ * See getMessage().
+ */
+ public abstract void onGetMessage(BluetoothMapMessage msg);
- /**
- * Called when the messages listing is retrieved.
- * See getMessagesListing().
- */
- public abstract void onGetMessagesListing(List<BluetoothMapMessagesListing> msgsListing);
+ /**
+ * Called when the messages listing is retrieved.
+ * See getMessagesListing().
+ */
+ public abstract void onGetMessagesListing(List<BluetoothMapMessagesListing> msgsListing);
- /**
- * Called when an event has occured.
- * See BluetoothMapEventReport for a description of what an event may look like.
- */
- public abstract void onEvent(BluetoothMapEventReport eventReport);
+ /**
+ * Called when an event has occured.
+ * See BluetoothMapEventReport for a description of what an event may look like.
+ */
+ public abstract void onEvent(BluetoothMapEventReport eventReport);
- }
+ }
- /**
- * Bind to the service and register the callback passed in the constructor.
- */
- public boolean connect() {
- checkMainThread();
+ /**
+ * Bind to the service and register the callback passed in the constructor.
+ */
+ public boolean connect() {
+ checkMainThread();
- if (mConnectionState != DISCONNECTED && mConnectionState != SUSPENDED) {
- Log.w(TAG, "Not in disconnected state, connection will eventually resume: " +
- mConnectionState);
- return true;
- }
- mConnectionState = CONNECTING;
- mServiceConnection = new BluetoothMapServiceConnection();
+ if (mConnectionState != DISCONNECTED && mConnectionState != SUSPENDED) {
+ Log.w(TAG, "Not in disconnected state, connection will eventually resume: " +
+ mConnectionState);
+ return true;
+ }
+ mConnectionState = CONNECTING;
+ mServiceConnection = new BluetoothMapServiceConnection();
- boolean bound = false;
- ComponentName cName =
- new ComponentName(
- "com.google.android.auto.mapservice",
- "com.google.android.auto.mapservice.BluetoothMapService");
- final Intent intent = new Intent();
- intent.setComponent(cName);
- try {
- bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
- } catch (Exception ex) {
- Log.e(TAG, "Failed binding to service." + ex);
- dumpState();
- return false;
- }
+ boolean bound = false;
+ ComponentName cName =
+ new ComponentName(
+ "com.google.android.auto.mapservice",
+ "com.google.android.auto.mapservice.BluetoothMapService");
+ final Intent intent = new Intent();
+ intent.setComponent(cName);
+ try {
+ bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed binding to service." + ex);
+ dumpState();
+ return false;
+ }
- if (!bound) {
- forceCloseConnection();
- dumpState();
- return false;
- }
+ if (!bound) {
+ forceCloseConnection();
+ dumpState();
+ return false;
+ }
- dumpState();
- return true;
- }
+ dumpState();
+ return true;
+ }
- /**
- * Unregister the callbacks and unbind from the service.
- */
- public void disconnect() {
- checkMainThread();
+ /**
+ * Unregister the callbacks and unbind from the service.
+ */
+ public void disconnect() {
+ checkMainThread();
- if (DBG) {
- Log.d(TAG, "Calling IBluetoothMapService.disconnect ...");
- }
+ if (DBG) {
+ Log.d(TAG, "Calling IBluetoothMapService.disconnect ...");
+ }
- // In case the manager is already disconnected, we don't need to do anything more here.
- if (mServiceBinder != null) {
- try {
- mServiceBinder.disconnect(mServiceCallbacks);
- } catch (RemoteException ex) {
- Log.w(TAG, "RemoteException during disconnect for " + mServiceConnection);
- } catch (IllegalStateException ex) {
- sendError(ex);
- }
- }
- forceCloseConnection();
- dumpState();
- }
+ // In case the manager is already disconnected, we don't need to do anything more here.
+ if (mServiceBinder != null) {
+ try {
+ mServiceBinder.disconnect(mServiceCallbacks);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "RemoteException during disconnect for " + mServiceConnection);
+ } catch (IllegalStateException ex) {
+ sendError(ex);
+ }
+ }
+ forceCloseConnection();
+ dumpState();
+ }
- /**
- * Enable notifications.
- */
- public void enableNotifications(boolean status) {
- checkMainThread();
+ /**
+ * Enable notifications.
+ */
+ public void enableNotifications(boolean status) {
+ checkMainThread();
- if (DBG) {
- Log.d(TAG, "Calling IBluetoothMapService.enableNotifications ..." + status);
- }
+ if (DBG) {
+ Log.d(TAG, "Calling IBluetoothMapService.enableNotifications ..." + status);
+ }
- if (mConnectionState != CONNECTED) {
- if (DBG) {
- Log.d(TAG, "Not connected to service.");
- }
- throw new IllegalStateException(
- "Service is not connected, either connect() is not called or a disconnect " +
- "event is not handled correctly.");
- }
+ if (mConnectionState != CONNECTED) {
+ if (DBG) {
+ Log.d(TAG, "Not connected to service.");
+ }
+ throw new IllegalStateException(
+ "Service is not connected, either connect() is not called or a disconnect " +
+ "event is not handled correctly.");
+ }
- if (!mOpInflight.equals(OP_NONE)) {
- throw new IllegalStateException(
- TAG + "Operation already in flight: " + mOpInflight +
- ". Please wait for an appropriate callback from your previous operation.");
- }
+ if (!mOpInflight.equals(OP_NONE)) {
+ throw new IllegalStateException(
+ TAG + "Operation already in flight: " + mOpInflight +
+ ". Please wait for an appropriate callback from your previous operation.");
+ }
- try {
- mServiceBinder.enableNotifications(mServiceCallbacks, status);
- } catch (RemoteException ex) {
- // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
- Log.e(TAG, "", ex);
- return;
- } catch (IllegalStateException ex) {
- sendError(ex);
- }
- mOpInflight = OP_ENABLE_NOTIFICATIONS;
- }
+ try {
+ mServiceBinder.enableNotifications(mServiceCallbacks, status);
+ } catch (RemoteException ex) {
+ // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
+ Log.e(TAG, "", ex);
+ return;
+ } catch (IllegalStateException ex) {
+ sendError(ex);
+ }
+ mOpInflight = OP_ENABLE_NOTIFICATIONS;
+ }
- /**
- * Push a message.
- */
- public void pushMessage(BluetoothMapMessage msg) {
- checkMainThread();
+ /**
+ * Push a message.
+ */
+ public void pushMessage(BluetoothMapMessage msg) {
+ checkMainThread();
- if (mConnectionState != CONNECTED) {
- throw new IllegalStateException(
- "Service is not connected, either connect() is not called or a disconnect " +
- "event is not handled correctly.");
- }
+ if (mConnectionState != CONNECTED) {
+ throw new IllegalStateException(
+ "Service is not connected, either connect() is not called or a disconnect " +
+ "event is not handled correctly.");
+ }
- if (!mOpInflight.equals(OP_NONE)) {
- throw new IllegalStateException(
- TAG + "Operation already in flight: " + mOpInflight +
- ". Please wait for an appropriate callback from your previous operation.");
- }
+ if (!mOpInflight.equals(OP_NONE)) {
+ throw new IllegalStateException(
+ TAG + "Operation already in flight: " + mOpInflight +
+ ". Please wait for an appropriate callback from your previous operation.");
+ }
- try {
- mServiceBinder.pushMessage(mServiceCallbacks, msg);
- } catch (RemoteException ex) {
- // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
- Log.e(TAG, "", ex);
- return;
- } catch (IllegalStateException ex) {
- sendError(ex);
- }
- mOpInflight = OP_PUSH_MESSAGE;
- }
+ try {
+ mServiceBinder.pushMessage(mServiceCallbacks, msg);
+ } catch (RemoteException ex) {
+ // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
+ Log.e(TAG, "", ex);
+ return;
+ } catch (IllegalStateException ex) {
+ sendError(ex);
+ }
+ mOpInflight = OP_PUSH_MESSAGE;
+ }
- /**
- * Get a message by its handle.
- */
- public void getMessage(String handle) {
- checkMainThread();
+ /**
+ * Get a message by its handle.
+ */
+ public void getMessage(String handle) {
+ checkMainThread();
- if (mConnectionState != CONNECTED) {
- throw new IllegalStateException(
- "Service is not connected, either connect() is not called or a disconnect " +
- "event is not handled correctly.");
- }
+ if (mConnectionState != CONNECTED) {
+ throw new IllegalStateException(
+ "Service is not connected, either connect() is not called or a disconnect " +
+ "event is not handled correctly.");
+ }
- if (!mOpInflight.equals(OP_NONE)) {
- throw new IllegalStateException(
- TAG + "Operation already in flight: " + mOpInflight +
- ". Please wait for an appropriate callback from your previous operation.");
- }
+ if (!mOpInflight.equals(OP_NONE)) {
+ throw new IllegalStateException(
+ TAG + "Operation already in flight: " + mOpInflight +
+ ". Please wait for an appropriate callback from your previous operation.");
+ }
- try {
- mServiceBinder.getMessage(mServiceCallbacks, handle);
- } catch (RemoteException ex) {
- // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
- Log.e(TAG, "", ex);
- return;
- } catch (IllegalStateException ex) {
- sendError(ex);
- }
- mOpInflight = OP_GET_MESSAGE;
- }
+ try {
+ mServiceBinder.getMessage(mServiceCallbacks, handle);
+ } catch (RemoteException ex) {
+ // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
+ Log.e(TAG, "", ex);
+ return;
+ } catch (IllegalStateException ex) {
+ sendError(ex);
+ }
+ mOpInflight = OP_GET_MESSAGE;
+ }
- public void getMessagesListing(String folder) {
- // If count is not specified the cap is put by the Bluetooth spec, we pass a large number
- // to use the cap provided by spec implementation on MAS server.
- getMessagesListing(folder, 65535, 0);
- }
- public void getMessagesListing(String folder, int count) {
- getMessagesListing(folder, count, 0);
- }
- public void getMessagesListing(String folder, int count, int offset) {
- checkMainThread();
+ public void getMessagesListing(String folder) {
+ // If count is not specified the cap is put by the Bluetooth spec, we pass a large number
+ // to use the cap provided by spec implementation on MAS server.
+ getMessagesListing(folder, 65535, 0);
+ }
+ public void getMessagesListing(String folder, int count) {
+ getMessagesListing(folder, count, 0);
+ }
+ public void getMessagesListing(String folder, int count, int offset) {
+ checkMainThread();
- if (mConnectionState != CONNECTED) {
- throw new IllegalStateException(
- "Service is not connected, either connect() is not called or a disconnect " +
- "event is not handled correctly.");
- }
+ if (mConnectionState != CONNECTED) {
+ throw new IllegalStateException(
+ "Service is not connected, either connect() is not called or a disconnect " +
+ "event is not handled correctly.");
+ }
- if (!mOpInflight.equals(OP_NONE)) {
- throw new IllegalStateException(
- TAG + "Operation already in flight: " + mOpInflight +
- ". Please wait for an appropriate callback from your previous operation.");
- }
+ if (!mOpInflight.equals(OP_NONE)) {
+ throw new IllegalStateException(
+ TAG + "Operation already in flight: " + mOpInflight +
+ ". Please wait for an appropriate callback from your previous operation.");
+ }
- try {
- mServiceBinder.getMessagesListing(mServiceCallbacks, folder, count, offset);
- } catch (RemoteException ex) {
- // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
- Log.e(TAG, "", ex);
- return;
- } catch (IllegalStateException ex) {
- sendError(ex);
- }
- mOpInflight = OP_GET_MESSAGES_LISTING;
- }
+ try {
+ mServiceBinder.getMessagesListing(mServiceCallbacks, folder, count, offset);
+ } catch (RemoteException ex) {
+ // If we have disconnected then the client will get a ERROR_CONNECT_SUSPENDED.
+ Log.e(TAG, "", ex);
+ return;
+ } catch (IllegalStateException ex) {
+ sendError(ex);
+ }
+ mOpInflight = OP_GET_MESSAGES_LISTING;
+ }
- /**
- * Checks if the current thread is main thread.
- *
- * Throws an IllegalStateException otherwise.
- */
- void checkMainThread() {
- if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
- throw new IllegalStateException(
- "Manager APIs should be called only from main thread.");
- }
- }
+ /**
+ * Checks if the current thread is main thread.
+ *
+ * Throws an IllegalStateException otherwise.
+ */
+ void checkMainThread() {
+ if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Manager APIs should be called only from main thread.");
+ }
+ }
- /**
- * Implements the callbacks when changes in the binder connection of Manager (this) and the
- * BluetoothMapService occur.
- * For a list of possible states that the binder connection can exist, see DISCONNECTED,
- * CONNECTING and CONNECTED states above.
- */
- private class BluetoothMapServiceConnection implements ServiceConnection {
- /**
- * Called when the Manager connects to service via the binder */
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- // onServiceConnected can be called either because we called onBind and in which case
- // connection state should already be CONNECTING, or it could be called because the
- // service came back up in which case we need to set it to CONNECTING (from
- // DISCONNECTED).
- if (mConnectionState == CONNECTED) {
- throw new IllegalStateException(
- "Cannot be in connected state while (re)connecting.");
- }
- // We may either be in CONNECTING or DISCONNECTED state here. Its safe to set to
- // CONNECTING in any scenario.
- mConnectionState = CONNECTING;
+ /**
+ * Implements the callbacks when changes in the binder connection of Manager (this) and the
+ * BluetoothMapService occur.
+ * For a list of possible states that the binder connection can exist, see DISCONNECTED,
+ * CONNECTING and CONNECTED states above.
+ */
+ private class BluetoothMapServiceConnection implements ServiceConnection {
+ /**
+ * Called when the Manager connects to service via the binder */
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ // onServiceConnected can be called either because we called onBind and in which case
+ // connection state should already be CONNECTING, or it could be called because the
+ // service came back up in which case we need to set it to CONNECTING (from
+ // DISCONNECTED).
+ if (mConnectionState == CONNECTED) {
+ throw new IllegalStateException(
+ "Cannot be in connected state while (re)connecting.");
+ }
+ // We may either be in CONNECTING or DISCONNECTED state here. Its safe to set to
+ // CONNECTING in any scenario.
+ mConnectionState = CONNECTING;
- if (DBG) {
- Log.d(TAG, "BluetoothMapServiceConnection.onServiceConnected name=" +
- name + " binder=" + binder);
- }
+ if (DBG) {
+ Log.d(TAG, "BluetoothMapServiceConnection.onServiceConnected name=" +
+ name + " binder=" + binder);
+ }
- // Save the binder for future calls to service.
- mServiceBinder = IBluetoothMapService.Stub.asInterface(binder);
+ // Save the binder for future calls to service.
+ mServiceBinder = IBluetoothMapService.Stub.asInterface(binder);
// Register the callbacks to the service.
mServiceCallbacks = new ServiceCallbacks(BluetoothMapManager.this);
@@ -688,3 +688,4 @@
Log.d(TAG, "dumpState(). Connection State: " + mConnectionState);
}
}
+
diff --git a/car-cluster-demo-renderer/res/layout/instrument_cluster.xml b/car-cluster-demo-renderer/res/layout/instrument_cluster.xml
index 7a22e53..64fb4ae 100644
--- a/car-cluster-demo-renderer/res/layout/instrument_cluster.xml
+++ b/car-cluster-demo-renderer/res/layout/instrument_cluster.xml
@@ -23,7 +23,7 @@
<LinearLayout
android:orientation="vertical"
android:layout_width="214dp"
- android:layout_height="match_parent" android:layout_weight="0.14">
+ android:layout_height="match_parent">
</LinearLayout>
<LinearLayout
android:orientation="vertical"
@@ -64,4 +64,33 @@
/>
</LinearLayout>
</LinearLayout>
+ <LinearLayout android:orientation="vertical" android:layout_width="wrap_content"
+ android:layout_height="match_parent" android:layout_weight="0.63"
+ android:layout_gravity="center_vertical" android:weightSum="1"
+ android:layout_marginTop="0dp">
+ <LinearLayout android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="wrap_content" android:layout_weight="0.63"
+ android:layout_gravity="center_horizontal" android:weightSum="1"
+ android:layout_marginTop="100dp"
+ android:background="@android:color/background_dark"
+ android:id="@+id/media_layout">
+ <ImageView android:layout_width="160dp" android:layout_height="100dp"
+ android:id="@+id/media_image" android:layout_gravity="center_horizontal"
+ android:background="@android:color/holo_blue_light"
+ android:scaleType="center"/>
+ <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:id="@+id/media_album" android:gravity="center"
+ android:layout_gravity="center_horizontal" android:visibility="gone"/>
+ <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:id="@+id/media_track" android:gravity="center"
+ android:layout_gravity="center_horizontal" android:textSize="28sp"/>
+ <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:id="@+id/media_artist" android:gravity="center"
+ android:textIsSelectable="false"
+ />
+ </LinearLayout>
+ </LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java
index 8bfd2d4..f3874d3 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java
@@ -15,86 +15,47 @@
*/
package android.car.cluster.demorenderer;
-import android.car.cluster.InstrumentClusterRenderer;
-import android.car.cluster.NavigationRenderer;
import android.car.navigation.CarNavigationInstrumentCluster;
+import android.car.cluster.renderer.DisplayConfiguration;
+import android.car.cluster.renderer.InstrumentClusterRenderer;
+import android.car.cluster.renderer.MediaRenderer;
+import android.car.cluster.renderer.NavigationRenderer;
import android.content.Context;
-import android.os.Looper;
-import android.util.Log;
-import android.view.Display;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import android.view.View;
/**
* Demo implementation of {@code InstrumentClusterRenderer}.
*/
public class DemoInstrumentClusterRenderer extends InstrumentClusterRenderer {
- private final static String TAG = DemoInstrumentClusterRenderer.class.getSimpleName();
-
- private static int TIMEOUT_MS = 5000;
private DemoInstrumentClusterView mView;
- private DemoPresentation mPresentation;
- private CountDownLatch mPresentationCreatedLatch = new CountDownLatch(1);
- private Looper mUiLooper;
+ private Context mContext;
@Override
- public void onCreate(final Context context, final Display display) {
- new Thread() {
- @Override
- public void run() {
- Log.d(TAG, "UI thread started.");
- Looper.prepare();
- mPresentation = new DemoPresentation(context, display);
- mView = new DemoInstrumentClusterView(mPresentation.getContext());
- mPresentation.setContentView(mView);
- mPresentationCreatedLatch.countDown();
- mUiLooper = Looper.myLooper();
- Looper.loop();
- }
- }.start();
- }
-
- private boolean waitForPresentation() {
- try {
- boolean ready = mPresentationCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
- if (!ready) {
- Log.w(TAG, "Presentation was not created within " + TIMEOUT_MS + "ms.",
- new RuntimeException() /* for stack trace */);
- }
- return ready;
- } catch (InterruptedException e) {
- Log.e(TAG, "Presentation creation interrupted.", e);
- throw new IllegalStateException(e);
- }
+ public void onCreate(Context context) {
+ mContext = context;
}
@Override
- public void onStart() {
- if (!waitForPresentation()) {
- return;
- }
-
- mPresentation.show();
+ public View onCreateView(DisplayConfiguration displayConfiguration) {
+ mView = new DemoInstrumentClusterView(mContext);
+ return mView;
}
@Override
- public void onStop() {
- if (!waitForPresentation()) {
- return;
- }
+ public void onStart() { }
- mPresentation.dismiss();
- }
+ @Override
+ public void onStop() { }
@Override
protected NavigationRenderer createNavigationRenderer() {
- if (!waitForPresentation()) {
- return null;
- }
+ return new DemoNavigationRenderer(mView);
+ }
- return new DemoNavigationRenderer(mView, mUiLooper);
+ @Override
+ protected MediaRenderer createMediaRenderer() {
+ return new DemoMediaRenderer(mView);
}
@Override
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java
index 2e6ef30..fbec788 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java
@@ -20,12 +20,13 @@
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.TextView;
/**
* This class is responsible for drawing the whole instrument cluster.
*/
-public class DemoInstrumentClusterView extends FrameLayout{
+public class DemoInstrumentClusterView extends FrameLayout {
private final String TAG = DemoInstrumentClusterView.class.getSimpleName();
@@ -33,6 +34,11 @@
private TextView eventTitleView;
private TextView distanceView;
private View navPanel;
+ private TextView mediaArtistView;
+ private TextView mediaAlbumView;
+ private TextView mediaTrackView;
+ private ImageView mediaImageView;
+ private View mediaPanel;
public DemoInstrumentClusterView(Context context) {
super(context);
@@ -78,6 +84,27 @@
distanceView.setText(distance);
}
+ public void setMediaData(final CharSequence artist, final CharSequence album,
+ final CharSequence track, final Bitmap image) {
+ Log.d(TAG, "setMediaData" + " artist = " + artist + ", album: " + album + ", track: " +
+ track + ", bitmap: " + image);
+
+ mediaArtistView.setText(artist);
+ mediaAlbumView.setText(album);
+ mediaTrackView.setText(track);
+ mediaImageView.setImageBitmap(image);
+ }
+
+ public void showMedia() {
+ Log.d(TAG, "showMedia");
+ mediaPanel.setVisibility(VISIBLE);
+ }
+
+ public void hideMedia() {
+ Log.d(TAG, "hideMedia");
+ mediaPanel.setVisibility(INVISIBLE);
+ }
+
private void init() {
Log.d(TAG, "init");
View rootView = inflate(getContext(), R.layout.instrument_cluster, null);
@@ -86,6 +113,12 @@
distanceView = (TextView) rootView.findViewById(R.id.nav_distance);
navPanel = rootView.findViewById(R.id.nav_layout);
+ mediaArtistView = (TextView) rootView.findViewById(R.id.media_artist);
+ mediaAlbumView = (TextView) rootView.findViewById(R.id.media_album);
+ mediaTrackView = (TextView) rootView.findViewById(R.id.media_track);
+ mediaImageView = (ImageView) rootView.findViewById(R.id.media_image);
+ mediaPanel = rootView.findViewById(R.id.media_layout);
+
setSpeed("0");
addView(rootView);
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java
new file mode 100644
index 0000000..4e4703a
--- /dev/null
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java
@@ -0,0 +1,78 @@
+/*
+ * 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.car.cluster.demorenderer;
+
+import android.car.cluster.renderer.MediaRenderer;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
+import android.util.Log;
+
+/**
+ * Demo of rendering media data in instrument cluster.
+ */
+public class DemoMediaRenderer extends MediaRenderer {
+
+ private static final String TAG = DemoMediaRenderer.class.getSimpleName();
+
+
+ private final DemoInstrumentClusterView mView;
+
+ private static final String[] PREFERRED_BITMAP_ORDER = {
+ MediaMetadata.METADATA_KEY_ALBUM_ART,
+ MediaMetadata.METADATA_KEY_ART,
+ MediaMetadata.METADATA_KEY_DISPLAY_ICON
+ };
+
+
+ public DemoMediaRenderer(DemoInstrumentClusterView view) {
+ mView = view;
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackState playbackState) {
+ Log.d(TAG, "onPlaybackStateChanged: " + playbackState);
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ Log.d(TAG, "onMetadataChanged: " + metadata);
+ if (metadata != null) {
+ CharSequence artist = metadata.getText(MediaMetadata.METADATA_KEY_ARTIST);
+ CharSequence album = metadata.getText(MediaMetadata.METADATA_KEY_ALBUM);
+ CharSequence track = metadata.getText(MediaMetadata.METADATA_KEY_TITLE);
+ Bitmap bitmap = getMetadataBitmap(metadata);
+
+ mView.setMediaData(artist, album, track, bitmap);
+ mView.showMedia();
+ } else {
+ mView.setMediaData(null, null, null, null);
+ mView.hideMedia();
+ }
+ }
+
+ private Bitmap getMetadataBitmap(MediaMetadata metadata) {
+ // Get the best art bitmap we can find
+ for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
+ Bitmap bitmap = metadata.getBitmap(PREFERRED_BITMAP_ORDER[i]);
+ if (bitmap != null) {
+ return bitmap;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java
index a1deddf..5ddb822 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java
@@ -19,11 +19,9 @@
import static android.car.navigation.CarNavigationManager.TURN_SIDE_RIGHT;
import static android.car.navigation.CarNavigationManager.TURN_TURN;
-import android.car.cluster.NavigationRenderer;
+import android.car.cluster.renderer.NavigationRenderer;
import android.content.Context;
import android.graphics.Bitmap;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import android.util.Pair;
@@ -38,7 +36,6 @@
private static final String TAG = DemoNavigationRenderer.class.getSimpleName();
private final DemoInstrumentClusterView mView;
- private final Handler mHandler;
private final Context mContext;
private final static Map<Pair<Integer, Integer>, Integer> sTurns;
@@ -50,30 +47,19 @@
// TODO: add more localized strings here.
}
- DemoNavigationRenderer(DemoInstrumentClusterView view, Looper looper) {
+ DemoNavigationRenderer(DemoInstrumentClusterView view) {
mView = view;
- mHandler = new Handler(looper);
mContext = view.getContext();
}
@Override
public void onStartNavigation() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mView.showNavigation();
- }
- });
+ mView.showNavigation();
}
@Override
public void onStopNavigation() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mView.hideNavigation();
- }
- });
+ mView.hideNavigation();
}
@Override
@@ -83,30 +69,12 @@
final String localizedTitle = String.format(
mContext.getString(R.string.nav_event_title_format), localizedAction, road);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mView.setNextTurn(image, localizedTitle);
- }
- });
+ mView.setNextTurn(image, localizedTitle);
}
@Override
public void onNextTurnDistanceChanged(final int distanceMeters, int timeSeconds) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mView.setNextTurnDistance(toHumanReadableDistance(distanceMeters));
- }
- });
- }
-
- public void runOnUiThread(Runnable action) {
- if (Looper.myLooper() == mHandler.getLooper()) {
- action.run();
- } else {
- mHandler.post(action);
- }
+ mView.setNextTurnDistance(toHumanReadableDistance(distanceMeters));
}
private String getLocalizedNavigationAction(int event, int turnSide) {
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java
index 4477c21..744d550 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java
+++ b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java
@@ -15,7 +15,7 @@
*/
package android.car.cluster.demorenderer;
-import android.car.cluster.InstrumentClusterRenderer;
+import android.car.cluster.renderer.InstrumentClusterRenderer;
/**
* This factory class will be requested by android.car using reflection in order to get an instance
diff --git a/car-lib/Android.mk b/car-lib/Android.mk
index 9af9498..4ab6af6 100644
--- a/car-lib/Android.mk
+++ b/car-lib/Android.mk
@@ -14,6 +14,9 @@
#
#
+#disble build in PDK, missing aidl import breaks build
+ifneq ($(TARGET_BUILD_PDK),true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -24,3 +27,5 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
include $(BUILD_JAVA_LIBRARY)
+
+endif #TARGET_BUILD_PDK
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index cb55d07..93ad7cf 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -29,13 +29,13 @@
import android.os.Looper;
import android.os.RemoteException;
import android.car.content.pm.CarPackageManager;
+import android.car.hardware.camera.CarCameraManager;
import android.car.hardware.CarSensorManager;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.radio.CarRadioManager;
import android.car.media.CarAudioManager;
import android.car.navigation.CarNavigationManager;
import android.car.test.CarTestManagerBinderWrapper;
-import android.car.CarLibLog;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -43,8 +43,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -79,6 +77,9 @@
public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service";
@SystemApi
+ public static final String CAMERA_SERVICE = "camera";
+
+ @SystemApi
public static final String RADIO_SERVICE = "radio";
@SystemApi
@@ -110,6 +111,10 @@
public static final String PERMISSION_CONTROL_APP_BLOCKING =
"android.car.permission.CONTROL_APP_BLOCKING";
+ /** Permission necessary to access Car Camera APIs. */
+ @SystemApi
+ public static final String PERMISSION_CAR_CAMERA = "android.car.permission.CAR_CAMERA";
+
/** Permission necessary to access Car HVAC APIs. */
@SystemApi
public static final String PERMISSION_CAR_HVAC = "android.car.permission.CAR_HVAC";
@@ -125,9 +130,15 @@
/** Type of car connection: platform runs directly in car. */
public static final int CONNECTION_TYPE_EMBEDDED = 5;
+ /**
+ * Type of car connection: platform runs directly in car but with mocked vehicle hal.
+ * This will only happen in testing environment.
+ * @hide
+ */
+ public static final int CONNECTION_TYPE_EMBEDDED_MOCKING = 6;
/** @hide */
- @IntDef({CONNECTION_TYPE_EMBEDDED})
+ @IntDef({CONNECTION_TYPE_EMBEDDED, CONNECTION_TYPE_EMBEDDED_MOCKING})
@Retention(RetentionPolicy.SOURCE)
public @interface ConnectionType {}
@@ -185,23 +196,6 @@
@GuardedBy("mCarManagerLock")
private final HashMap<String, CarManagerBase> mServiceMap = new HashMap<>();
- private final ICarConnectionListenerImpl mICarConnectionListenerImpl =
- new ICarConnectionListenerImpl(this);
- /**
- * CarConnectionListener which did not get connected notification yet.
- */
- private final HashSet<CarConnectionListener> mCarConnectionNotConnectedListeners =
- new HashSet<>();
- /**
- * CarConnectionListener which got connected notification already. Only listener with
- * connected notification will get disconnected notification when disconnect event happens.
- */
- private final HashSet<CarConnectionListener> mCarConnectionConnectedListeners =
- new HashSet<>();
- /** CarConnectionListener to get current event. */
- private final LinkedList<CarConnectionListener> mCarConnectionListenersForEvent =
- new LinkedList<>();
-
/** Handler for generic event dispatching. */
private final Handler mEventHandler;
@@ -324,14 +318,6 @@
}
/**
- * Tells if connected to car. Same as isConnected. Necessary for compatibility with support lib.
- * @return
- */
- public boolean isConnectedToCar() {
- return isConnected();
- }
-
- /**
* Get car specific service as in {@link Context#getSystemService(String)}. Returned
* {@link Object} should be type-casted to the desired service.
* For example, to get sensor service,
@@ -378,59 +364,6 @@
}
/**
- * Registers a {@link CarConnectionListener}.
- *
- * Avoid reregistering unregistered listeners. If an unregistered listener is reregistered,
- * it may receive duplicate calls to {@link CarConnectionListener#onConnected}.
- *
- * @throws IllegalStateException if service is not connected.
- */
- public void registerCarConnectionListener(CarConnectionListener listener)
- throws IllegalStateException {
- ICar service = getICarOrThrow();
- synchronized (this) {
- if (mCarConnectionNotConnectedListeners.size() == 0 &&
- mCarConnectionConnectedListeners.size() == 0) {
- try {
- service.registerCarConnectionListener(mICarConnectionListenerImpl);
- } catch (RemoteException e) {
- // ignore
- }
- }
- mCarConnectionNotConnectedListeners.add(listener);
- }
- mEventHandler.post(new Runnable() {
- @Override
- public void run() {
- handleCarConnected();
- }
- });
- }
-
- /**
- * Unregisters a {@link CarConnectionListener}.
- *
- * <b>Note:</b> If this method is called from a thread besides the client's looper thread,
- * there is no guarantee that the unregistered listener will not receive callbacks after
- * this method returns.
- */
- public void unregisterCarConnectionListener(CarConnectionListener listener) {
- synchronized (this) {
- mCarConnectionNotConnectedListeners.remove(listener);
- mCarConnectionConnectedListeners.remove(listener);
- if (mCarConnectionNotConnectedListeners.size() == 0 &&
- mCarConnectionConnectedListeners.size() == 0) {
- try {
- ICar service = getICarOrThrow();
- service.unregisterCarConnectionListener(mICarConnectionListenerImpl);
- } catch (IllegalStateException | RemoteException e) {
- // ignore
- }
- }
- }
- }
-
- /**
* IllegalStateException from XyzCarService with special message is re-thrown as a different
* exception. If the IllegalStateException is not understood then this message will throw the
* original exception.
@@ -453,7 +386,7 @@
CarManagerBase manager = null;
switch (serviceName) {
case AUDIO_SERVICE:
- manager = new CarAudioManager(binder);
+ manager = new CarAudioManager(binder, mContext);
break;
case SENSOR_SERVICE:
manager = new CarSensorManager(binder, mContext, mLooper);
@@ -470,6 +403,9 @@
case CAR_NAVIGATION_SERVICE:
manager = new CarNavigationManager(binder, mLooper);
break;
+ case CAMERA_SERVICE:
+ manager = new CarCameraManager(binder, mContext);
+ break;
case HVAC_SERVICE:
manager = new CarHvacManager(binder, mContext, mLooper);
break;
@@ -513,65 +449,4 @@
mServiceMap.clear();
}
}
-
- private void handleCarConnected() {
- synchronized (this) {
- mCarConnectionListenersForEvent.clear();
- mCarConnectionConnectedListeners.addAll(mCarConnectionNotConnectedListeners);
- mCarConnectionListenersForEvent.addAll(mCarConnectionNotConnectedListeners);
- mCarConnectionNotConnectedListeners.clear();
- }
- for (CarConnectionListener listener : mCarConnectionListenersForEvent) {
- listener.onConnected(CONNECTION_TYPE_EMBEDDED);
- }
- }
-
- private void handleCarDisconnected() {
- synchronized (this) {
- mCarConnectionListenersForEvent.clear();
- mCarConnectionNotConnectedListeners.addAll(mCarConnectionConnectedListeners);
- mCarConnectionListenersForEvent.addAll(mCarConnectionConnectedListeners);
- mCarConnectionConnectedListeners.clear();
- }
- for (CarConnectionListener listener : mCarConnectionListenersForEvent) {
- listener.onDisconnected();
- }
- }
-
- private static class ICarConnectionListenerImpl extends ICarConnectionListener.Stub {
-
- private final WeakReference<Car> mCar;
-
- private ICarConnectionListenerImpl(Car car) {
- mCar = new WeakReference<>(car);
- }
-
- @Override
- public void onConnected() {
- final Car car = mCar.get();
- if (car == null) {
- return;
- }
- car.mEventHandler.post(new Runnable() {
- @Override
- public void run() {
- car.handleCarConnected();
- }
- });
- }
-
- @Override
- public void onDisconnected() {
- final Car car = mCar.get();
- if (car == null) {
- return;
- }
- car.mEventHandler.post(new Runnable() {
- @Override
- public void run() {
- car.handleCarDisconnected();
- }
- });
- }
- }
}
diff --git a/car-lib/src/android/car/CarConnectionListener.java b/car-lib/src/android/car/CarConnectionListener.java
deleted file mode 100644
index 603f393..0000000
--- a/car-lib/src/android/car/CarConnectionListener.java
+++ /dev/null
@@ -1,33 +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;
-
-/**
- * Listener for monitoring car's connection status.
- * Callbacks are called from the looper specified when constructing {@link Car}.
- */
-public interface CarConnectionListener {
- /**
- * Car has been connected. Does not guarantee that the car is still connected whilst this
- * callback is running, so {@link CarNotConnectedException}s may still be thrown from
- * {@link Car} method calls.
- * @param connectionType Type of car connected.
- */
- void onConnected(@Car.ConnectionType int connectionType);
- /** Car disconnected */
- void onDisconnected();
-}
diff --git a/car-lib/src/android/car/ICar.aidl b/car-lib/src/android/car/ICar.aidl
index 01551bc..1510438 100644
--- a/car-lib/src/android/car/ICar.aidl
+++ b/car-lib/src/android/car/ICar.aidl
@@ -23,7 +23,5 @@
/** @hide */
interface ICar {
IBinder getCarService(in String serviceName) = 0;
- boolean isConnectedToCar() = 1;
- void registerCarConnectionListener(in ICarConnectionListener listener) = 2;
- void unregisterCarConnectionListener(in ICarConnectionListener listener) = 3;
+ int getCarConnectionType() = 1;
}
diff --git a/car-lib/src/android/car/app/menu/CarMenuCallbacks.java b/car-lib/src/android/car/app/menu/CarMenuCallbacks.java
new file mode 100644
index 0000000..4e44428
--- /dev/null
+++ b/car-lib/src/android/car/app/menu/CarMenuCallbacks.java
@@ -0,0 +1,83 @@
+/*
+ * 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.car.app.menu;
+
+import android.os.Bundle;
+
+/**
+ * The callbacks that a car app needs to pass to a car ui provider for car menu interactions.
+ */
+public abstract class CarMenuCallbacks {
+ /**
+ * Called when the car menu 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 getRootMenu(Bundle hints);
+
+ /**
+ * Query for information about the menu items that are contained within
+ * the specified id and subscribes to receive updates when they change.
+ *
+ * @param parentId The id of the parent menu item whose list of children
+ * will be subscribed.
+ * @param callback The callback to receive the list of children.
+ */
+ public abstract void subscribe(String parentId, SubscriptionCallbacks callback);
+
+ /**
+ * Unsubscribe for changes to the children of the specified id.
+ * @param parentId The id of the parent menu item whose list of children
+ * will be unsubscribed.
+ */
+ public abstract void unsubscribe(String parentId, SubscriptionCallbacks callbacks);
+
+ /**
+ * Called when the car menu has been opened.
+ */
+ public abstract void onCarMenuOpened();
+
+ /**
+ * Called when the car menu has been closed.
+ */
+ public abstract void onCarMenuClosed();
+
+ /**
+ * Called when a car menu item with the specified id has been clicked.
+ */
+ public abstract void onItemClicked(String id);
+
+ /**
+ * Called when a car menu item with the specified id has been long clicked.
+ */
+ public abstract boolean onItemLongClicked(String id);
+
+ /**
+ * Called when the menu button is clicked.
+ */
+ public abstract boolean onMenuClicked();
+
+ /**
+ * Called when the menu is opening.
+ */
+ public abstract void onCarMenuOpening();
+
+ /**
+ * Called when the menu is closing.
+ */
+ public abstract void onCarMenuClosing();
+}
diff --git a/car-lib/src/android/car/app/menu/CarMenuConstants.java b/car-lib/src/android/car/app/menu/CarMenuConstants.java
new file mode 100644
index 0000000..fe4b7fe
--- /dev/null
+++ b/car-lib/src/android/car/app/menu/CarMenuConstants.java
@@ -0,0 +1,112 @@
+/*
+ * 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.car.app.menu;
+
+import android.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.
+ */
+public class CarMenuConstants {
+ 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";
+ }
+}
\ No newline at end of file
diff --git a/car-lib/src/android/car/app/menu/CarUiEntry.java b/car-lib/src/android/car/app/menu/CarUiEntry.java
index b4968c8..c91e486 100644
--- a/car-lib/src/android/car/app/menu/CarUiEntry.java
+++ b/car-lib/src/android/car/app/menu/CarUiEntry.java
@@ -18,9 +18,8 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.view.View;
+import android.widget.EditText;
/**
* A base class for a car ui entry which is used for loading and manipulating common car
@@ -35,64 +34,184 @@
* the set of must have functions in a CarUiProvider.
*/
public abstract class CarUiEntry {
- protected Context mAppContext;
- protected Context mUiLibContext;
+ protected final Context mAppContext;
+ protected final Context mUiLibContext;
public CarUiEntry(Context uiLibContext, Context appContext) {
- mAppContext = appContext;
mUiLibContext = uiLibContext.createConfigurationContext(
appContext.getResources().getConfiguration());
+ mAppContext = appContext;
}
+ /**
+ * Return the content view.
+ */
abstract public View getContentView();
- abstract public void setCarMenuBinder(IBinder binder) throws RemoteException;
+ /**
+ * Set {@link android.car.app.menu.CarMenuCallbacks} from a car app for car menu interactions.
+ */
+ abstract public void setCarMenuCallbacks(CarMenuCallbacks callbacks);
+ /**
+ * Return the id of the main container in which app can render its own content.
+ */
abstract public int getFragmentContainerId();
+ /**
+ * Set the background bitmap.
+ */
abstract public void setBackground(Bitmap bitmap);
+ /**
+ * Set the background image resource.
+ */
abstract public void setBackgroundResource(int resId);
+ /**
+ * Replace the menu button with the given bitmap.
+ */
+ abstract public void setMenuButtonBitmap(Bitmap bitmap);
+
+ /**
+ * Hide the menu button.
+ */
abstract public void hideMenuButton();
+ /**
+ * Restore the menu button.
+ */
abstract public void restoreMenuDrawable();
- abstract public void setMenuProgress(float progress);
-
+ /**
+ * Set the color of the car menu scrim.
+ */
abstract public void setScrimColor(int color);
+ /**
+ * Set the title of the car menu.
+ */
abstract public void setTitle(CharSequence title);
- abstract public void setTitleText(CharSequence title);
-
+ /**
+ * Close the car menu.
+ */
abstract public void closeDrawer();
+ /**
+ * Open the car menu.
+ */
abstract public void openDrawer();
+ /**
+ * Show the menu associated with the specified id, and set the car menu title.
+ */
abstract public void showMenu(String id, String title);
+ /**
+ * Set the car menu button color.
+ */
abstract public void setMenuButtonColor(int color);
+ /**
+ * Make the menu title visible.
+ */
abstract public void showTitle();
+ /**
+ * Hide the menu title.
+ */
abstract public void hideTitle();
+ /**
+ * Use the light car theme.
+ */
abstract public void setLightMode();
+ /**
+ * Use the dark car theme.
+ */
abstract public void setDarkMode();
+ /**
+ * Use automatic light/dark car theme based on ui mode.
+ */
abstract public void setAutoLightDarkMode();
+ /**
+ * Called when the activity's onRestoreInstanceState is called.
+ */
abstract public void onRestoreInstanceState(Bundle savedInstanceState);
+ /**
+ * Called when the activity's onSaveInstanceState is called.
+ */
abstract public void onSaveInstanceState(Bundle outState);
+ /**
+ * Show the search box and set the click listener for the search box.
+ */
+ abstract public void showSearchBox(View.OnClickListener listener);
+
+ /**
+ * Set the color of the search box.
+ */
+ abstract public void setSearchBoxColors(int backgroundColor, int searchLogoColor,
+ int textColor, int hintTextColor);
+
+ /**
+ * Set the search box edit listener for monitoring input.
+ */
+ abstract public void setSearchBoxEditListener(SearchBoxEditListener listener);
+
+ /**
+ * Called when activity's onStart is called.
+ */
abstract public void onStart();
+ /**
+ * Called when activity's onResume is called.
+ */
abstract public void onResume();
+ /**
+ * Called when activity's onPause is called.
+ */
abstract public void onPause();
+ /**
+ * Called when activity's onStop is called.
+ */
abstract public void onStop();
+
+ /**
+ * Start input on the search box and show IME.
+ * @param hint hint text to show in the search box.
+ * @param searchBoxClickListener search box click listener.
+ * @return The search box {@link android.widget.EditText}.
+ */
+ abstract public EditText startInput(String hint,
+ View.OnClickListener searchBoxClickListener);
+
+ /**
+ * Set the view in the end of the search box as the search result is loading.
+ */
+ abstract public void setSearchBoxEndView(View view);
+
+ /**
+ * Returns the current user entered text in the search box.
+ */
+ abstract public CharSequence getSearchBoxText();
+
+ /**
+ * Called when input should be stopped.
+ */
+ abstract public void stopInput();
+
+ /**
+ * Show a toast message.
+ * @param msg text to show
+ * @param duration toast duration in millisecond.
+ */
+ abstract public void showToast(String msg, long duration);
}
diff --git a/car-support-lib/src/android/support/car/app/menu/Root.java b/car-lib/src/android/car/app/menu/RootMenu.java
similarity index 72%
copy from car-support-lib/src/android/support/car/app/menu/Root.java
copy to car-lib/src/android/car/app/menu/RootMenu.java
index fbdf0e7..30eca92 100644
--- a/car-support-lib/src/android/support/car/app/menu/Root.java
+++ b/car-lib/src/android/car/app/menu/RootMenu.java
@@ -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.
@@ -13,25 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.support.car.app.menu;
+package android.car.app.menu;
import android.os.Bundle;
/**
- * Stores the Root id for the menu. The Root is the main menu.
+ * 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.
*/
-public class Root {
+public class RootMenu {
private final Bundle mBundle;
+ private final String mRootId;
/**
* Create a root with no extra hints.
*
* @param id Root id
*/
- public Root(String id) {
+ public RootMenu(String id) {
this(id, null);
}
@@ -41,12 +41,9 @@
* @param id Root id
* @param extras Hints to pass along
*/
- public Root(String id, Bundle extras) {
- mBundle = new Bundle();
- mBundle.putString(Constants.CarMenuConstants.KEY_ID, id);
- if (extras != null) {
- mBundle.putAll(extras);
- }
+ public RootMenu(String id, Bundle extras) {
+ mRootId = id;
+ mBundle = extras;
}
/**
@@ -55,7 +52,7 @@
* @return The root id
*/
public String getId() {
- return mBundle.getString(Constants.CarMenuConstants.KEY_ID);
+ return mRootId;
}
/**
diff --git a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl b/car-lib/src/android/car/app/menu/SearchBoxEditListener.java
similarity index 81%
rename from car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
rename to car-lib/src/android/car/app/menu/SearchBoxEditListener.java
index b8b6c55..c7057d3 100644
--- a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
+++ b/car-lib/src/android/car/app/menu/SearchBoxEditListener.java
@@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.app.menu;
+package android.car.app.menu;
-interface ISearchBoxEditListener {
+public abstract class SearchBoxEditListener {
/**
* The user hit enter on the keyboard.
*/
- void onSearch(in String text) = 0;
+ public abstract void onSearch(String text);
/**
* The user changed the text in the search box with the keyboard.
*/
- void onEdit(in String text) = 1;
+ public abstract void onEdit(String text);
}
diff --git a/car-lib/src/android/car/app/menu/SubscriptionCallbacks.java b/car-lib/src/android/car/app/menu/SubscriptionCallbacks.java
new file mode 100644
index 0000000..9902436
--- /dev/null
+++ b/car-lib/src/android/car/app/menu/SubscriptionCallbacks.java
@@ -0,0 +1,46 @@
+/*
+ * 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.car.app.menu;
+
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * The callbacks to receive menu items updates.
+ */
+public abstract class SubscriptionCallbacks {
+ /**
+ * Called when the items with the specified parent id are loaded.
+ *
+ * @param items The list of menu items. To retrieve the content of the item, use the keys
+ * defined in {@link CarMenuConstants.CarMenuConstants}.
+ */
+ public abstract void onChildrenLoaded(String parentId, List<Bundle> items);
+
+ /**
+ * Called when there is an error loading the items with the specified id.
+ */
+ public abstract void onError(String id);
+
+ /**
+ * Called when the car menu items with the specified parent id are changed.
+ *
+ * @param item The new menu item. To retrieve the content of the item, use the keys
+ * defined in {@link CarMenuConstants.CarMenuConstants}.
+ */
+ public abstract void onChildChanged(String parentId, Bundle item);
+}
\ No newline at end of file
diff --git a/car-lib/src/android/car/cluster/InstrumentClusterRenderer.java b/car-lib/src/android/car/cluster/InstrumentClusterRenderer.java
deleted file mode 100644
index 737cbb8..0000000
--- a/car-lib/src/android/car/cluster/InstrumentClusterRenderer.java
+++ /dev/null
@@ -1,59 +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.car.cluster;
-
-import android.annotation.SystemApi;
-import android.car.navigation.CarNavigationInstrumentCluster;
-import android.content.Context;
-import android.view.Display;
-
-/**
- * Interface for instrument cluster rendering.
- *
- * TODO: implement instrument cluster feature list and extend API.
- *
- * @hide
- */
-@SystemApi
-public abstract class InstrumentClusterRenderer {
-
- private NavigationRenderer mNavigationRenderer;
-
- /**
- * Calls once when instrument cluster should be created.
- * @param context
- * @param display
- */
- abstract public void onCreate(Context context, Display display);
-
- abstract public void onStart();
-
- abstract public void onStop();
-
- /**
- * Returns properties of instrument cluster for navigation.
- */
- abstract public CarNavigationInstrumentCluster getNavigationProperties();
-
- abstract protected NavigationRenderer createNavigationRenderer();
-
- public NavigationRenderer getNavigationRenderer() {
- if (mNavigationRenderer == null) {
- mNavigationRenderer = createNavigationRenderer();
- }
- return mNavigationRenderer;
- }
-}
diff --git a/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java b/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java
new file mode 100644
index 0000000..2b9f0a0
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java
@@ -0,0 +1,96 @@
+/*
+ * 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.car.cluster.renderer;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * TODO: need to properly define this class and make it immutable.
+ *
+ * @hide
+ */
+@SystemApi
+public class DisplayConfiguration implements Parcelable {
+ private final Rect mPrimaryRegion;
+
+ @Nullable
+ private final Rect mSecondaryRegion;
+
+ public static final Parcelable.Creator<DisplayConfiguration> CREATOR =
+ new Parcelable.Creator<DisplayConfiguration>() {
+
+ public DisplayConfiguration createFromParcel(Parcel in) {
+ return new DisplayConfiguration(in);
+ }
+
+ public DisplayConfiguration[] newArray(int size) {
+ return new DisplayConfiguration[size];
+ }
+ };
+
+
+ public DisplayConfiguration(Parcel in) {
+ mPrimaryRegion = in.readTypedObject(Rect.CREATOR);
+ mSecondaryRegion = in.readTypedObject(Rect.CREATOR);
+ }
+
+ public DisplayConfiguration(Rect primaryRegion) {
+ this(primaryRegion, null);
+ }
+
+ public DisplayConfiguration(Rect primaryRegion, @Nullable Rect secondaryRegion) {
+ mPrimaryRegion = primaryRegion;
+ mSecondaryRegion = secondaryRegion;
+ }
+
+
+ /** Region that will be fully visible in instrument cluster */
+ public Rect getPrimaryRegion() {
+ return mPrimaryRegion;
+ }
+
+ /**
+ * The region that includes primary region + may include some additional space that might
+ * be partially visible in the instrument cluster. It is useful to fade-out primary
+ * rendering for example or adding background image.
+ */
+ @Nullable
+ public Rect getSecondaryRegion() {
+ return mSecondaryRegion;
+ }
+
+ /** Returns true if secondary region is strongly greater then primary region */
+ public boolean hasSecondaryRegion() {
+ return mSecondaryRegion != null
+ && mSecondaryRegion.width() > mPrimaryRegion.width()
+ && mSecondaryRegion.height() > mPrimaryRegion.height();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedObject(mPrimaryRegion, 0);
+ dest.writeTypedObject(mSecondaryRegion, 0);
+ }
+}
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderer.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderer.java
new file mode 100644
index 0000000..77eac4b
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderer.java
@@ -0,0 +1,84 @@
+/*
+ * 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.car.cluster.renderer;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.UiThread;
+import android.car.navigation.CarNavigationInstrumentCluster;
+import android.content.Context;
+import android.view.View;
+
+/**
+ * Interface for instrument cluster rendering.
+ *
+ * TODO: implement instrument cluster feature list and extend API.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class InstrumentClusterRenderer {
+
+ @Nullable private NavigationRenderer mNavigationRenderer;
+ @Nullable private MediaRenderer mMediaRenderer;
+
+ /**
+ * Calls once when instrument cluster should be created.
+ */
+ abstract public void onCreate(Context context);
+
+ @UiThread
+ abstract public View onCreateView(DisplayConfiguration displayConfiguration);
+
+ @UiThread
+ abstract public void onStart();
+
+ @UiThread
+ abstract public void onStop();
+
+ /**
+ * Returns properties of instrument cluster for navigation.
+ */
+ abstract public CarNavigationInstrumentCluster getNavigationProperties();
+
+ @UiThread
+ abstract protected NavigationRenderer createNavigationRenderer();
+
+ @UiThread
+ abstract protected MediaRenderer createMediaRenderer();
+
+ /** The method is thread-safe, callers should cache returned object. */
+ @Nullable
+ public synchronized NavigationRenderer getNavigationRenderer() {
+ return mNavigationRenderer;
+ }
+
+ /** The method is thread-safe, callers should cache returned object. */
+ @Nullable
+ public synchronized MediaRenderer getMediaRenderer() {
+ return mMediaRenderer;
+ }
+
+ /**
+ * This method is called by car service after onCreateView to initialize private members. The
+ * method should not be overridden by subclasses.
+ */
+ @UiThread
+ public synchronized final void initialize() {
+ mNavigationRenderer = createNavigationRenderer();
+ mMediaRenderer = createMediaRenderer();
+ }
+}
diff --git a/car-lib/src/android/car/cluster/renderer/MediaRenderer.java b/car-lib/src/android/car/cluster/renderer/MediaRenderer.java
new file mode 100644
index 0000000..4620cc3
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/MediaRenderer.java
@@ -0,0 +1,33 @@
+/*
+ * 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.car.cluster.renderer;
+
+import android.annotation.SystemApi;
+import android.annotation.UiThread;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
+
+/**
+ * Represents renderer of current media status in instrument cluster.
+ *
+ * @hide
+ */
+@SystemApi
+@UiThread
+public abstract class MediaRenderer {
+ public abstract void onPlaybackStateChanged(PlaybackState playbackState);
+ public abstract void onMetadataChanged(MediaMetadata metadata);
+}
diff --git a/car-lib/src/android/car/cluster/NavigationRenderer.java b/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
similarity index 93%
rename from car-lib/src/android/car/cluster/NavigationRenderer.java
rename to car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
index 810f6e0..67eac5e 100644
--- a/car-lib/src/android/car/cluster/NavigationRenderer.java
+++ b/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.car.cluster;
+package android.car.cluster.renderer;
import android.annotation.SystemApi;
+import android.annotation.UiThread;
import android.graphics.Bitmap;
/**
@@ -25,6 +26,7 @@
* @hide
*/
@SystemApi
+@UiThread
public abstract class NavigationRenderer {
abstract public void onStartNavigation();
abstract public void onStopNavigation();
diff --git a/car-lib/src/android/car/hardware/CarSensorEvent.java b/car-lib/src/android/car/hardware/CarSensorEvent.java
index afc655f..d99e84d 100644
--- a/car-lib/src/android/car/hardware/CarSensorEvent.java
+++ b/car-lib/src/android/car/hardware/CarSensorEvent.java
@@ -16,11 +16,8 @@
package android.car.hardware;
-import android.location.GpsSatellite;
-import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemClock;
/**
@@ -43,12 +40,12 @@
public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0;
/**
* Index in {@link #floatValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
- * sensor. This value is fuel level in coverable distance.
+ * sensor. This value is fuel level in coverable distance. The unit is Km.
*/
public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1;
/**
- * Index in {@link #byteValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
- * sensor. This value is set to 0 if fuel low level warning is on.
+ * Index in {@link #intValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
+ * sensor. This value is set to 1 if fuel low level warning is on.
*/
public static final int INDEX_FUEL_LOW_WARNING = 0;
@@ -110,19 +107,6 @@
public static final int DRIVE_STATUS_FULLY_RESTRICTED = DRIVE_STATUS_NO_VIDEO |
DRIVE_STATUS_NO_KEYBOARD_INPUT | DRIVE_STATUS_NO_VOICE_INPUT | DRIVE_STATUS_NO_CONFIG |
DRIVE_STATUS_LIMIT_MESSAGE_LEN;
- /**
- * Index for {@link CarSensorManager#SENSOR_TYPE_LOCATION} in floatValues.
- * Each bit intValues[0] represents whether the corresponding data is present.
- */
- public static final int INDEX_LOCATION_LATITUDE = 0;
- public static final int INDEX_LOCATION_LONGITUDE = 1;
- public static final int INDEX_LOCATION_ACCURACY = 2;
- public static final int INDEX_LOCATION_ALTITUDE = 3;
- public static final int INDEX_LOCATION_SPEED = 4;
- public static final int INDEX_LOCATION_BEARING = 5;
- public static final int INDEX_LOCATION_MAX = INDEX_LOCATION_BEARING;
- public static final int INDEX_LOCATION_LATITUDE_INTS = 1;
- public static final int INDEX_LOCATION_LONGITUDE_INTS = 2;
/**
* Index for {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT} in floatValues.
@@ -135,52 +119,6 @@
*/
public static final int INDEX_ENVIRONMENT_PRESSURE = 1;
- /**
- * Indices for {@link CarSensorManager#SENSOR_TYPE_COMPASS} in floatValues.
- * Angles are in degrees. Pitch or/and roll can be NaN if it is not available.
- */
- public static final int INDEX_COMPASS_BEARING = 0;
- public static final int INDEX_COMPASS_PITCH = 1;
- public static final int INDEX_COMPASS_ROLL = 2;
-
- /**
- * Indices for {@link CarSensorManager#SENSOR_TYPE_ACCELEROMETER} in floatValues.
- * Acceleration (gravity) is in m/s^2. Any component can be NaN if it is not available.
- */
- public static final int INDEX_ACCELEROMETER_X = 0;
- public static final int INDEX_ACCELEROMETER_Y = 1;
- public static final int INDEX_ACCELEROMETER_Z = 2;
-
- /**
- * Indices for {@link CarSensorManager#SENSOR_TYPE_GYROSCOPE} in floatValues.
- * Rotation speed is in rad/s. Any component can be NaN if it is not available.
- */
- public static final int INDEX_GYROSCOPE_X = 0;
- public static final int INDEX_GYROSCOPE_Y = 1;
- public static final int INDEX_GYROSCOPE_Z = 2;
-
- /**
- * Indices for {@link CarSensorManager#SENSOR_TYPE_GPS_SATELLITE}.
- * Both byte values and float values are used.
- * Two first bytes encode number of satellites in-use/in-view (or 0xFF if unavailable).
- * Then optionally with INDEX_GPS_SATELLITE_ARRAY_BYTE_OFFSET offset and interval
- * INDEX_GPS_SATELLITE_ARRAY_BYTE_INTERVAL between elements are encoded boolean flags of whether
- * particular satellite from in-view participate in in-use subset.
- * Float values with INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET offset and interval
- * INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL between elements can optionally contain
- * per-satellite values of signal strength and other values or NaN if unavailable.
- */
- public static final int INDEX_GPS_SATELLITE_NUMBER_IN_USE = 0;
- public static final int INDEX_GPS_SATELLITE_NUMBER_IN_VIEW = 1;
- public static final int INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET = 2;
- public static final int INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL = 1;
- public static final int INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET = 0;
- public static final int INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL = 4;
- public static final int INDEX_GPS_SATELLITE_PRN_OFFSET = 0;
- public static final int INDEX_GPS_SATELLITE_SNR_OFFSET = 1;
- public static final int INDEX_GPS_SATELLITE_AZIMUTH_OFFSET = 2;
- public static final int INDEX_GPS_SATELLITE_ELEVATION_OFFSET = 3;
-
private static final long MILLI_IN_NANOS = 1000000L;
/** Sensor type for this event like {@link CarSensorManager#SENSOR_TYPE_CAR_SPEED}. */
@@ -357,10 +295,10 @@
public static class FuelLevelData {
public long timeStampNs;
- /** If unsupported by the car, this value is 0. */
+ /** Fuel level in %. If unsupported by the car, this value is -1. */
public int level;
- /** If unsupported by the car, this value is 0. */
- public int range;
+ /** Fuel as possible range in Km. If unsupported by the car, this value is -1. */
+ public float range;
/** If unsupported by the car, this value is false. */
public boolean lowFuelWarning;
}
@@ -379,8 +317,21 @@
data = new FuelLevelData();
}
data.timeStampNs = timeStampNs;
- data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
- data.range = (int) floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
+ if (floatValues == null) {
+ data.level = -1;
+ data.range = -1;
+ } else {
+ if (floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE] < 0) {
+ data.level = -1;
+ } else {
+ data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
+ }
+ if (floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE] < 0) {
+ data.range = -1;
+ } else {
+ data.range = floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
+ }
+ }
data.lowFuelWarning = intValues[0] == 1;
return data;
}
@@ -454,81 +405,6 @@
return data;
}
- public static class CompassData {
- public long timeStampNs;
- /** If unsupported by the car, this value is NaN. */
- public float bearing;
- /** If unsupported by the car, this value is NaN. */
- public float pitch;
- /** If unsupported by the car, this value is NaN. */
- public float roll;
- }
-
- /**
- * Convenience method for obtaining a {@link CompassData} object from a CarSensorEvent
- * object with type {@link CarSensorManager#SENSOR_TYPE_COMPASS}.
- *
- * @param data an optional output parameter which, if non-null, will be used by this method
- * instead of a newly created object.
- * @return a CompassData object corresponding to the data contained in the CarSensorEvent.
- */
- public CompassData getCompassData(CompassData data) {
- checkType(CarSensorManager.SENSOR_TYPE_COMPASS);
- if (data == null) {
- data = new CompassData();
- }
- data.bearing = floatValues[INDEX_COMPASS_BEARING];
- data.pitch = floatValues[INDEX_COMPASS_PITCH];
- data.roll = floatValues[INDEX_COMPASS_ROLL];
- return data;
- }
-
- /**
- * Convenience method for obtaining a {@link Location} object from a CarSensorEvent
- * object with type {@link CarSensorManager#SENSOR_TYPE_LOCATION}.
- *
- * @param location an optional output parameter which, if non-null, will be used by this method
- * instead of a newly created object.
- * @return a Location object corresponding to the data contained in the CarSensorEvent.
- */
- public Location getLocation(Location location) {
- checkType(CarSensorManager.SENSOR_TYPE_LOCATION);
- if (location == null) {
- location = new Location("Car-GPS");
- }
- // intValues[0]: bit flags for the presence of other values following.
- int presense = intValues[0];
- if ((presense & (0x1 << INDEX_LOCATION_LATITUDE)) != 0) {
- int latE7 = intValues[INDEX_LOCATION_LATITUDE_INTS];
- location.setLatitude(latE7 * 1e-7);
- }
- if ((presense & (0x1 << INDEX_LOCATION_LONGITUDE)) != 0) {
- int longE7 = intValues[INDEX_LOCATION_LONGITUDE_INTS];
- location.setLongitude(longE7 * 1e-7);
- }
- if ((presense & (0x1 << INDEX_LOCATION_ACCURACY)) != 0) {
- location.setAccuracy(floatValues[INDEX_LOCATION_ACCURACY]);
- }
- if ((presense & (0x1 << INDEX_LOCATION_ALTITUDE)) != 0) {
- location.setAltitude(floatValues[INDEX_LOCATION_ALTITUDE]);
- }
- if ((presense & (0x1 << INDEX_LOCATION_SPEED)) != 0) {
- location.setSpeed(floatValues[INDEX_LOCATION_SPEED]);
- }
- if ((presense & (0x1 << INDEX_LOCATION_BEARING)) != 0) {
- location.setBearing(floatValues[INDEX_LOCATION_BEARING]);
- }
- location.setElapsedRealtimeNanos(timeStampNs);
- // There is a risk of scheduler delaying 2nd elapsedRealtimeNs value.
- // But will not try to fix it assuming that is acceptable as UTC time's accuracy is not
- // guaranteed in Location data.
- long currentTimeMs = System.currentTimeMillis();
- long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
- location.setTime(
- currentTimeMs - (elapsedRealtimeNs - timeStampNs) / MILLI_IN_NANOS);
- return location;
- }
-
public static class DrivingStatusData {
public long timeStampNs;
public int status;
@@ -551,153 +427,6 @@
return data;
}
- public static class AccelerometerData {
- public long timeStampNs;
- /** If unsupported by the car, this value is NaN. */
- public float x;
- /** If unsupported by the car, this value is NaN. */
- public float y;
- /** If unsupported by the car, this value is NaN. */
- public float z;
- }
-
- /**
- * Convenience method for obtaining an {@link AccelerometerData} object from a CarSensorEvent
- * object with type {@link CarSensorManager#SENSOR_TYPE_ACCELEROMETER}.
- *
- * @param data an optional output parameter which, if non-null, will be used by this method
- * instead of a newly created object.
- * @return a AccelerometerData object corresponding to the data contained in the CarSensorEvent.
- */
- public AccelerometerData getAccelerometerData(AccelerometerData data) {
- checkType(CarSensorManager.SENSOR_TYPE_ACCELEROMETER);
- if (data == null) {
- data = new AccelerometerData();
- }
- data.x = floatValues[INDEX_ACCELEROMETER_X];
- data.y = floatValues[INDEX_ACCELEROMETER_Y];
- data.z = floatValues[INDEX_ACCELEROMETER_Z];
- return data;
- }
-
- public static class GyroscopeData {
- public long timeStampNs;
- /** If unsupported by the car, this value is NaN. */
- public float x;
- /** If unsupported by the car, this value is NaN. */
- public float y;
- /** If unsupported by the car, this value is NaN. */
- public float z;
- }
-
- /**
- * Convenience method for obtaining a {@link GyroscopeData} object from a CarSensorEvent
- * object with type {@link CarSensorManager#SENSOR_TYPE_GYROSCOPE}.
- *
- * @param data an optional output parameter which, if non-null, will be used by this method
- * instead of a newly created object.
- * @return a GyroscopeData object corresponding to the data contained in the CarSensorEvent.
- */
- public GyroscopeData getGyroscopeData(GyroscopeData data) {
- checkType(CarSensorManager.SENSOR_TYPE_GYROSCOPE);
- if (data == null) {
- data = new GyroscopeData();
- }
- data.x = floatValues[INDEX_GYROSCOPE_X];
- data.y = floatValues[INDEX_GYROSCOPE_Y];
- data.z = floatValues[INDEX_GYROSCOPE_Z];
- return data;
- }
-
- // android.location.GpsSatellite doesn't have a public constructor, so that can't be used.
- /**
- * Class that contains GPS satellite status. For more info on meaning of these fields refer
- * to the documentation to the {@link GpsSatellite} class.
- */
- public static class GpsSatelliteData {
- public long timeStampNs;
- /**
- * Number of satellites used in GPS fix or -1 of unavailable.
- */
- public int numberInUse = -1;
- /**
- * Number of satellites in view or -1 of unavailable.
- */
- public int numberInView = -1;
- /**
- * Per-satellite flag if this satellite was used for GPS fix.
- * Can be null if per-satellite data is unavailable.
- */
- public boolean[] usedInFix = null;
- /**
- * Per-satellite pseudo-random id.
- * Can be null if per-satellite data is unavailable.
- */
- public int[] prn = null;
- /**
- * Per-satellite signal to noise ratio.
- * Can be null if per-satellite data is unavailable.
- */
- public float[] snr = null;
- /**
- * Per-satellite azimuth.
- * Can be null if per-satellite data is unavailable.
- */
- public float[] azimuth = null;
- /**
- * Per-satellite elevation.
- * Can be null if per-satellite data is unavailable.
- */
- public float[] elevation = null;
- }
-
- /**
- * Convenience method for obtaining a {@link GpsSatelliteData} object from a CarSensorEvent
- * object with type {@link CarSensorManager#SENSOR_TYPE_HVAC} with optional per-satellite info.
- *
- * @param data an optional output parameter which, if non-null, will be used by this method
- * instead of a newly created object.
- * @param withPerSatellite whether to include per-satellite data.
- * @return a GpsSatelliteData object corresponding to the data contained in the CarSensorEvent.
- */
- public GpsSatelliteData getGpsSatelliteData(GpsSatelliteData data,
- boolean withPerSatellite) {
- checkType(CarSensorManager.SENSOR_TYPE_GPS_SATELLITE);
- if (data == null) {
- data = new GpsSatelliteData();
- }
- final int intOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET;
- final int intInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL;
- final int floatOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET;
- final int floatInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL;
- final int numberOfSats = (floatValues.length - floatOffset) / floatInterval;
-
- data.numberInUse = intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_USE];
- data.numberInView = intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_VIEW];
- if (withPerSatellite && data.numberInView >= 0) {
- data.usedInFix = new boolean[numberOfSats];
- data.prn = new int[numberOfSats];
- data.snr = new float[numberOfSats];
- data.azimuth = new float[numberOfSats];
- data.elevation = new float[numberOfSats];
-
- for (int i = 0; i < numberOfSats; ++i) {
- int iInt = intOffset + intInterval * i;
- int iFloat = floatOffset + floatInterval * i;
- data.usedInFix[i] = intValues[iInt] != 0;
- data.prn[i] = Math.round(
- floatValues[iFloat + CarSensorEvent.INDEX_GPS_SATELLITE_PRN_OFFSET]);
- data.snr[i] =
- floatValues[iFloat + CarSensorEvent.INDEX_GPS_SATELLITE_SNR_OFFSET];
- data.azimuth[i] = floatValues[iFloat
- + CarSensorEvent.INDEX_GPS_SATELLITE_AZIMUTH_OFFSET];
- data.elevation[i] = floatValues[iFloat
- + CarSensorEvent.INDEX_GPS_SATELLITE_ELEVATION_OFFSET];
- }
- }
- return data;
- }
-
/** @hide */
@Override
public String toString() {
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index 1573d69..fedf3de 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -42,13 +42,8 @@
* API for monitoring car sensor data.
*/
public class CarSensorManager implements CarManagerBase {
- /**
- * SENSOR_TYPE_* represents type of sensor supported from the connected car.
- * This sensor represents the direction of the car as an angle in degree measured clockwise
- * with 0 degree pointing to north.
- * Sensor data in {@link CarSensorEvent} is a float (floatValues[0]).
- */
- public static final int SENSOR_TYPE_COMPASS = 1;
+ /** @hide */
+ public static final int SENSOR_TYPE_RESERVED1 = 1;
/**
* This sensor represents vehicle speed in m/s.
* Sensor data in {@link CarSensorEvent} is a float which will be >= 0.
@@ -88,55 +83,39 @@
* {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
*/
public static final int SENSOR_TYPE_GEAR = 7;
- public static final int SENSOR_TYPE_RESERVED8 = 8;
/**
* Day/night sensor. Sensor data is intValues[0].
*/
- public static final int SENSOR_TYPE_NIGHT = 9;
- /**
- * Sensor type for location. Sensor data passed in floatValues.
- */
- public static final int SENSOR_TYPE_LOCATION = 10;
+ public static final int SENSOR_TYPE_NIGHT = 8;
+ /** @hide */
+ public static final int SENSOR_TYPE_REVERVED9 = 9;
/**
* Represents the current driving status of car. Different user interaction should be used
* depending on the current driving status. Driving status is intValues[0].
*/
- public static final int SENSOR_TYPE_DRIVING_STATUS = 11;
+ public static final int SENSOR_TYPE_DRIVING_STATUS = 10;
/**
* Environment like temperature and pressure.
*/
- public static final int SENSOR_TYPE_ENVIRONMENT = 12;
-
+ public static final int SENSOR_TYPE_ENVIRONMENT = 11;
+ /** @hide */
+ public static final int SENSOR_TYPE_RESERVED12 = 12;
/** @hide */
public static final int SENSOR_TYPE_RESERVED13 = 13;
/** @hide */
- public static final int SENSOR_TYPE_ACCELEROMETER = 14;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED15 = 15;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED16 = 16;
- /** @hide */
- public static final int SENSOR_TYPE_GPS_SATELLITE = 17;
- /** @hide */
- public static final int SENSOR_TYPE_GYROSCOPE = 18;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED19 = 19;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED20 = 20;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED21 = 21;
+ public static final int SENSOR_TYPE_RESERVED14 = 14;
/**
* Sensor type bigger than this is invalid. Always update this after adding a new sensor.
* @hide
*/
- private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_RESERVED21;
+ private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_ENVIRONMENT;
/**
* Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
* {@link #SENSOR_TYPE_VENDOR_EXTENSION_END}] is for each car vendor's to use.
* This should be only used for system app to access sensors not defined as standard types.
- * So the sensor supproted in this range can vary depending on car models / manufacturers.
+ * So the sensor supported in this range can vary depending on car models / manufacturers.
* 3rd party apps should not use sensors in this range as they are not compatible across
* different cars. Additionally 3rd party apps trying to access sensor in this range will get
* security exception as their access is restricted to system apps.
@@ -272,11 +251,9 @@
* If the same listener is registered again for the same sensor, it will be either ignored or
* updated depending on the rate.
* <p>
- * Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} for
- * {@link #SENSOR_TYPE_LOCATION}, {@link Car#PERMISSION_SPEED} for
- * {@link #SENSOR_TYPE_CAR_SPEED}, {@link Car#PERMISSION_MILEAGE} for
- * {@link #SENSOR_TYPE_ODOMETER}, or {@link Car#PERMISSION_FUEL} for
- * {@link #SENSOR_TYPE_FUEL_LEVEL}.
+ * Requires {@link Car#PERMISSION_SPEED} for {@link #SENSOR_TYPE_CAR_SPEED},
+ * {@link Car#PERMISSION_MILEAGE} for {@link #SENSOR_TYPE_ODOMETER},
+ * or {@link Car#PERMISSION_FUEL} for {@link #SENSOR_TYPE_FUEL_LEVEL}.
*
* @param listener
* @param sensorType sensor type to subscribe.
@@ -388,7 +365,9 @@
}
/**
- * Get the most recent CarSensorEvent for the given type.
+ * Get the most recent CarSensorEvent for the given type. Note that latest sensor data from car
+ * will not be available if it was never subscribed before. This call will return immediately
+ * with null if there is no data available.
* @param type A sensor to request
* @return null if there was no sensor update since connected to the car.
* @throws CarNotConnectedException
diff --git a/car-lib/src/android/car/hardware/camera/CarCamera.java b/car-lib/src/android/car/hardware/camera/CarCamera.java
new file mode 100644
index 0000000..02be070
--- /dev/null
+++ b/car-lib/src/android/car/hardware/camera/CarCamera.java
@@ -0,0 +1,106 @@
+/*
+ * 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.car.hardware.camera;
+
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * API for controlling camera system in cars
+ */
+@SystemApi
+public class CarCamera {
+ public final static String TAG = CarCamera.class.getSimpleName();
+ public final int mCameraType;
+ private final ICarCamera mService;
+
+ public CarCamera(ICarCamera service, int cameraType) {
+ mService = service;
+ mCameraType = cameraType;
+ }
+
+ public int getCapabilities() {
+ int capabilities;
+ try {
+ capabilities = mService.getCapabilities(mCameraType);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getCapabilities", e);
+ capabilities = 0;
+ }
+ return capabilities;
+ }
+
+ public Rect getCameraCrop() {
+ Rect rect;
+ try {
+ rect = mService.getCameraCrop(mCameraType);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getCameraCrop", e);
+ rect = null;
+ }
+ return rect;
+ }
+
+ public void setCameraCrop(Rect rect) {
+ try {
+ mService.setCameraCrop(mCameraType, rect);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in setCameraCrop", e);
+ }
+ }
+
+ public Rect getCameraPosition() {
+ Rect rect;
+ try {
+ rect = mService.getCameraPosition(mCameraType);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getCameraPosition", e);
+ rect = null;
+ }
+ return rect;
+ }
+
+ public void setCameraPosition(Rect rect) {
+ try {
+ mService.setCameraPosition(mCameraType, rect);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in setCameraPosition", e);
+ }
+ }
+
+ public CarCameraState getCameraState() {
+ CarCameraState state;
+ try {
+ state = mService.getCameraState(mCameraType);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getCameraState", e);
+ state = null;
+ }
+ return state;
+ }
+
+ public void setCameraState(CarCameraState state) {
+ try {
+ mService.setCameraState(mCameraType, state);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in setCameraState", e);
+ }
+ }
+}
+
diff --git a/car-lib/src/android/car/hardware/camera/CarCameraManager.java b/car-lib/src/android/car/hardware/camera/CarCameraManager.java
new file mode 100644
index 0000000..d531c95
--- /dev/null
+++ b/car-lib/src/android/car/hardware/camera/CarCameraManager.java
@@ -0,0 +1,109 @@
+/*
+ * 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.car.hardware.camera;
+
+import android.annotation.SystemApi;
+import android.car.Car;
+import android.car.CarManagerBase;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * API for controlling camera system in cars
+ */
+@SystemApi
+public class CarCameraManager implements CarManagerBase {
+ public final static boolean DBG = true;
+ public final static String TAG = CarCameraManager.class.getSimpleName();
+
+ // Camera capabilities flags
+ public static final int ANDROID_OVERLAY_SUPPORT_FLAG = 0x1;
+ public static final int CAMERA_CROP_SUPPORT_FLAG = 0x2;
+ public static final int CAMERA_POSITIONING_SUPPORT_FLAG = 0x4;
+
+ // Camera types
+ public static final int CAR_CAMERA_TYPE_NONE = 0;
+ public static final int CAR_CAMERA_TYPE_RVC = 1;
+
+ private int[] mCameraList;
+ private final ICarCamera mService;
+
+ /**
+ * Get an instance of the CarCameraManager.
+ *
+ * Should not be obtained directly by clients, use {@link Car.getCarManager()} instead.
+ * @hide
+ */
+ public CarCameraManager(IBinder service, Context context) {
+ mService = ICarCamera.Stub.asInterface(service);
+ try {
+ mCameraList = mService.getCameraList();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getCameraList", e);
+ mCameraList = null;
+ }
+ }
+
+ /**
+ *
+ * @return Array of CAR_CAMERA_TYPE_* telling which cameras are present
+ */
+ public int[] getCameraList() {
+ return mCameraList;
+ }
+
+ /**
+ *
+ * @param cameraType Camera type to query capabilites
+ * @return Bitmask of camera capabilities available for this device
+ */
+ public int getCameraCapabilities(int cameraType) {
+ int capabilities;
+ try {
+ capabilities = mService.getCapabilities(cameraType);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getCameraCapabilities", e);
+ capabilities = 0;
+ }
+ return capabilities;
+ }
+
+ public CarCamera openCamera(int cameraType) {
+ CarCamera camera = null;
+
+ // Find cameraType in the list of available cameras
+ for (int i : mCameraList) {
+ if(i == cameraType) {
+ camera = new CarCamera(mService, cameraType);
+ break;
+ }
+ }
+ return camera;
+ }
+
+ public void closeCamera(CarCamera camera) {
+ // TODO: What should we do?
+ }
+
+ /** @hide */
+ @Override
+ public void onCarDisconnected() {
+ }
+}
+
diff --git a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl b/car-lib/src/android/car/hardware/camera/CarCameraState.aidl
similarity index 67%
copy from car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
copy to car-lib/src/android/car/hardware/camera/CarCameraState.aidl
index b8b6c55..a9cf858 100644
--- a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
+++ b/car-lib/src/android/car/hardware/camera/CarCameraState.aidl
@@ -13,16 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.app.menu;
-interface ISearchBoxEditListener {
- /**
- * The user hit enter on the keyboard.
- */
- void onSearch(in String text) = 0;
+package android.car.hardware.camera;
- /**
- * The user changed the text in the search box with the keyboard.
- */
- void onEdit(in String text) = 1;
-}
+parcelable CarCameraState;
diff --git a/car-lib/src/android/car/hardware/camera/CarCameraState.java b/car-lib/src/android/car/hardware/camera/CarCameraState.java
new file mode 100644
index 0000000..6f25903
--- /dev/null
+++ b/car-lib/src/android/car/hardware/camera/CarCameraState.java
@@ -0,0 +1,98 @@
+/*
+ * 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.car.hardware.camera;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * CarCameraState object corresponds to a property of the car's Camera system
+ * @hide
+ */
+@SystemApi
+public class CarCameraState implements Parcelable {
+ private boolean mCameraIsOn;
+ private boolean mOverlayIsOn;
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCameraIsOn ? 1 : 0);
+ out.writeInt(mOverlayIsOn ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<CarCameraState> CREATOR
+ = new Parcelable.Creator<CarCameraState>() {
+ public CarCameraState createFromParcel(Parcel in) {
+ return new CarCameraState(in);
+ }
+
+ public CarCameraState[] newArray(int size) {
+ return new CarCameraState[size];
+ }
+ };
+
+ private CarCameraState(Parcel in) {
+ mCameraIsOn = in.readInt() == 1;
+ mOverlayIsOn = in.readInt() == 1;
+ }
+
+ /**
+ * Copy constructor
+ * @param that
+ */
+ public CarCameraState(CarCameraState that) {
+ mCameraIsOn = that.getCameraIsOn();
+ mOverlayIsOn = that.getOverlayIsOn();
+ }
+
+ /**
+ * Constructor
+ */
+ public CarCameraState(boolean overlayIsOn, boolean cameraIsOn) {
+ mCameraIsOn = cameraIsOn;
+ mOverlayIsOn = overlayIsOn;
+ }
+
+ // Getters.
+ public boolean getCameraIsOn() { return mCameraIsOn; }
+ public boolean getOverlayIsOn() { return mOverlayIsOn; }
+
+ // Setters.
+ public void setCameraIsOn(boolean v) { mCameraIsOn = v; }
+ public void setOverlayIsOn(boolean v) { mOverlayIsOn = v; }
+
+ // Printer.
+ public String toString() {
+ String myString = "mCameraIsOn: " + mCameraIsOn + " mOverlayIsOn: " + mOverlayIsOn;
+ return myString;
+ }
+
+ // Comparator.
+ public boolean equals(Object o) {
+ if (o instanceof CarCameraState) {
+ CarCameraState that = (CarCameraState) o;
+
+ return (that.getCameraIsOn() == mCameraIsOn &&
+ that.getOverlayIsOn() == mOverlayIsOn);
+ }
+ return false;
+ }
+}
diff --git a/car-lib/src/android/car/hardware/camera/ICarCamera.aidl b/car-lib/src/android/car/hardware/camera/ICarCamera.aidl
new file mode 100644
index 0000000..53eee36
--- /dev/null
+++ b/car-lib/src/android/car/hardware/camera/ICarCamera.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.car.hardware.camera;
+
+import android.car.hardware.camera.CarCameraState;
+import android.graphics.Rect;
+
+/** @hide */
+interface ICarCamera {
+ int[] getCameraList() = 0;
+
+ int getCapabilities(in int cameraType) = 1;
+
+ Rect getCameraCrop(in int cameraType) = 2;
+
+ void setCameraCrop(in int cameraType, in Rect rect) = 3;
+
+ Rect getCameraPosition(in int cameraType) = 4;
+
+ void setCameraPosition(in int cameraType, in Rect rect) = 5;
+
+ CarCameraState getCameraState(in int cameraType) = 6;
+
+ void setCameraState(in int cameraType, in CarCameraState state) = 7;
+}
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index 55c6ea2..7abe90d 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -16,7 +16,10 @@
package android.car.media;
import android.annotation.IntDef;
+import android.content.Context;
import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioManager.OnAudioFocusChangeListener;
import android.os.IBinder;
import android.os.RemoteException;
import android.car.CarManagerBase;
@@ -82,6 +85,7 @@
public @interface CarAudioUsage {}
private final ICarAudio mService;
+ private final AudioManager mAudioManager;
/**
* Get {@link AudioAttrbutes} relevant for the given usage in car.
@@ -98,13 +102,39 @@
}
}
+ /**
+ * Request audio focus.
+ * Send a request to obtain the audio focus.
+ * @param l
+ * @param requestAttributes
+ * @param durationHint
+ * @param flags
+ */
+ public int requestAudioFocus(OnAudioFocusChangeListener l,
+ AudioAttributes requestAttributes,
+ int durationHint,
+ int flags) throws IllegalArgumentException {
+ return mAudioManager.requestAudioFocus(l, requestAttributes, durationHint, flags);
+ }
+
+ /**
+ * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
+ * @param l
+ * @param aa
+ * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
+ */
+ public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
+ return mAudioManager.abandonAudioFocus(l, aa);
+ }
+
@Override
public void onCarDisconnected() {
// TODO Auto-generated method stub
}
/** @hide */
- public CarAudioManager(IBinder service) {
+ public CarAudioManager(IBinder service, Context context) {
mService = ICarAudio.Stub.asInterface(service);
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
}
diff --git a/car-lib/src/android/car/navigation/CarNavigationManager.java b/car-lib/src/android/car/navigation/CarNavigationManager.java
index 090980b..d1df559 100644
--- a/car-lib/src/android/car/navigation/CarNavigationManager.java
+++ b/car-lib/src/android/car/navigation/CarNavigationManager.java
@@ -148,10 +148,8 @@
* used for event type {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}. -1 if unused.
* @param turnNumber turn number, counting around from the roundabout entry to the exit. Only
* used for event type {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}. -1 if unused.
- * @param image image to be shown in the instrument cluster (PNG format). Null if instrument
- * cluster type is {@link #INSTRUMENT_CLUSTER_TYPE_ENUM}, or if
- * the image parameters are malformed (length or width non-positive, or illegal
- * imageColorDepthBits) in the initial NavigationStatusService call.
+ * @param image image to be shown in the instrument cluster. Null if instrument
+ * cluster type doesn't support images.
* @param turnSide turn side ({@link #TURN_SIDE_LEFT}, {@link #TURN_SIDE_RIGHT} or
* {@link #TURN_SIDE_UNSPECIFIED}).
* @return true if successful.
@@ -210,7 +208,7 @@
}
/**
- * @param listener {@link CarNavigationStatusListener} to be registered, replacing any existing
+ * @param listener {@link CarNavigationListener} to be registered, replacing any existing
* listeners.
* @throws CarNotConnectedException
*/
@@ -231,7 +229,7 @@
}
/**
- * Unregisters {@link CarNavigationStatusListener}.
+ * Unregisters {@link CarNavigationListener}.
*/
public void unregisterListener() {
try {
@@ -266,12 +264,11 @@
mHandler.sendMessage(mHandler.obtainMessage(STOP));
}
- private static class ICarNavigationEventListenerImpl extends
- ICarNavigationEventListener.Stub {
+ private static class ICarNavigationEventListenerImpl extends ICarNavigationEventListener.Stub {
private final WeakReference<CarNavigationManager> mManager;
public ICarNavigationEventListenerImpl(CarNavigationManager manager) {
- mManager = new WeakReference<CarNavigationManager>(manager);
+ mManager = new WeakReference<>(manager);
}
@Override
diff --git a/car-support-lib/src/android/support/car/Car.java b/car-support-lib/src/android/support/car/Car.java
index df2febd..42fc9b5 100644
--- a/car-support-lib/src/android/support/car/Car.java
+++ b/car-support-lib/src/android/support/car/Car.java
@@ -82,6 +82,12 @@
public static final int CONNECTION_TYPE_ADB_EMULATOR = 4;
/** Type of car connection: platform runs directly in car. */
public static final int CONNECTION_TYPE_EMBEDDED = 5;
+ /**
+ * Type of car connection: platform runs directly in car but with mocked vehicle hal.
+ * This will only happen in testing environment.
+ * @hide
+ */
+ public static final int CONNECTION_TYPE_EMBEDDED_MOCKING = 6;
/** @hide */
@IntDef({CONNECTION_TYPE_EMULATOR, CONNECTION_TYPE_USB, CONNECTION_TYPE_WIFI,
diff --git a/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java b/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
index 69e608c..bb02d87 100644
--- a/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
+++ b/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
@@ -19,8 +19,10 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Message;
import android.support.car.Car;
import android.support.car.content.pm.CarPackageManagerEmbedded;
import android.support.car.hardware.CarSensorManagerEmbedded;
@@ -50,13 +52,15 @@
};
private final android.car.Car mCar;
- private final LinkedList<CarConnectionListenerProxy> mCarConnectionListenerProxies =
+ private final LinkedList<CarConnectionListener> mCarConnectionListeners =
new LinkedList<>();
+ private final CallbackHandler mHandler;
public CarServiceLoaderEmbedded(Context context, ServiceConnectionListener listener,
Looper looper) {
super(context, listener, looper);
mCar = android.car.Car.createCar(context, mServiceConnection, looper);
+ mHandler = new CallbackHandler(looper);
}
@Override
@@ -71,7 +75,8 @@
@Override
public boolean isConnectedToCar() {
- return mCar.isConnectedToCar();
+ // for embedded, connected to service means connected to car.
+ return mCar.isConnected();
}
@Override
@@ -80,42 +85,18 @@
}
@Override
- public void registerCarConnectionListener(CarConnectionListener listener) {
- CarConnectionListenerProxy newProxy = null;
+ public void registerCarConnectionListener(final CarConnectionListener listener) {
synchronized (this) {
- boolean alreadyRegistered = false;
- for (CarConnectionListenerProxy proxy : mCarConnectionListenerProxies) {
- if (proxy.isSameListener(listener)) {
- alreadyRegistered = true;
- break;
- }
- }
- if (!alreadyRegistered) {
- newProxy = new CarConnectionListenerProxy(listener);
- mCarConnectionListenerProxies.add(newProxy);
- }
+ mCarConnectionListeners.add(listener);
}
- if (newProxy != null) {
- mCar.registerCarConnectionListener(newProxy);
- }
+ // car service connection is checked when this is called. So just dispatch it.
+ mHandler.dispatchCarConnectionCall(listener, getCarConnectionType());
}
@Override
public void unregisterCarConnectionListener(CarConnectionListener listener) {
- CarConnectionListenerProxy matchingProxy = null;
synchronized (this) {
- for (CarConnectionListenerProxy proxy : mCarConnectionListenerProxies) {
- if (proxy.isSameListener(listener)) {
- matchingProxy = proxy;
- break;
- }
- }
- if (matchingProxy != null) {
- mCarConnectionListenerProxies.remove(matchingProxy);
- }
- }
- if (matchingProxy != null) {
- mCar.unregisterCarConnectionListener(matchingProxy);
+ mCarConnectionListeners.remove(listener);
}
}
@@ -144,25 +125,26 @@
}
}
- private static class CarConnectionListenerProxy implements android.car.CarConnectionListener {
- private final CarConnectionListener mListener;
+ private static class CallbackHandler extends Handler {
- public CarConnectionListenerProxy(CarConnectionListener listener) {
- mListener = listener;
+ private static final int MSG_DISPATCH_CAR_CONNECTION = 0;
+
+ private CallbackHandler(Looper looper) {
+ super(looper);
}
- public boolean isSameListener(CarConnectionListener listener) {
- return mListener == listener;
+ private void dispatchCarConnectionCall(CarConnectionListener listener, int connectionType) {
+ sendMessage(obtainMessage(MSG_DISPATCH_CAR_CONNECTION, connectionType, 0, listener));
}
@Override
- public void onConnected(int connectionType) {
- mListener.onConnected(connectionType);
- }
-
- @Override
- public void onDisconnected() {
- mListener.onDisconnected();
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DISPATCH_CAR_CONNECTION:
+ CarConnectionListener listener = (CarConnectionListener) msg.obj;
+ listener.onConnected(msg.arg1);
+ break;
+ }
}
}
}
diff --git a/car-support-lib/src/android/support/car/app/CarAppUtil.java b/car-support-lib/src/android/support/car/app/CarAppUtil.java
new file mode 100644
index 0000000..2371025
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/CarAppUtil.java
@@ -0,0 +1,33 @@
+/*
+ * 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
index a24b308..41a2d2a 100644
--- a/car-support-lib/src/android/support/car/app/CarFragmentActivity.java
+++ b/car-support-lib/src/android/support/car/app/CarFragmentActivity.java
@@ -1,3 +1,18 @@
+/*
+ * 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;
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
index 92f0c6d..67d5f1f 100644
--- a/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java
+++ b/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java
@@ -19,43 +19,27 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.support.annotation.LayoutRes;
import android.support.car.Car;
import android.support.car.app.CarFragmentActivity;
-import android.support.car.input.CarEditable;
-import android.support.car.input.CarEditableListener;
+import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
import android.support.car.input.CarInputManager;
-import android.support.car.input.CarRestrictedEditText;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionWrapper;
-import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
/**
* Base class for a car app which wants to use a drawer.
*/
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 =
@@ -67,7 +51,7 @@
private final CarUiController mUiController;
private CarMenuCallbacks mMenuCallbacks;
- private CarMenuCallbacksBinder mBinder;
+ private OnMenuClickListener mMenuClickListener;
private boolean mDrawerShowing;
private boolean mShowingSearchBox;
private boolean mSearchBoxEnabled;
@@ -77,24 +61,29 @@
private CarInputManager mInputManager;
private EditText mSearchBoxView;
- /**
- * Simple interface to listen for keyboard events.
- */
- public interface SearchBoxEditListener {
+ public interface OnMenuClickListener {
/**
- * The user hit enter on the keyboard.
+ * 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.
*/
- void onSearch(String text);
-
- /**
- * The user changed the text in the search box with the keyboard.
- */
- void onEdit(String text);
+ boolean onClicked();
}
public CarDrawerActivity(Proxy proxy, Context context, Car car) {
super(proxy, context, car);
- mUiController = new CarUiController(this);
+ 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
@@ -110,16 +99,56 @@
inflater.inflate(resourceId, parent, true);
}
- public void setContentFragment(Fragment fragment) {
- super.setContentFragment(fragment, mUiController.getFragmentContainerId());
- }
-
@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);
@@ -144,27 +173,30 @@
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();
}
- 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();
- }
-
+ /**
+ * 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);
}
/**
@@ -173,7 +205,7 @@
* @param listener {@link OnMenuClickListener} that will listen for menu button clicks.
*/
public void setOnMenuClickedListener(OnMenuClickListener listener) {
- mBinder.setOnMenuClickedListener(listener);
+ mMenuClickListener = listener;
}
/**
@@ -239,6 +271,9 @@
mUiController.setBackgroundResource(resId);
}
+ /**
+ * Sets the color of the scrim to the right of the car menu drawer.
+ */
public void setScrimColor(int color) {
mUiController.setScrimColor(color);
}
@@ -253,8 +288,19 @@
mUiController.showMenu(id, title);
}
- private void registerCarMenuCallbacks(IBinder callbacks) {
- mUiController.registerCarMenuCallbacks(callbacks);
+ public boolean onMenuClicked() {
+ if (mMenuClickListener != null) {
+ mMenuClickListener.onClicked();
+ return true;
+ }
+ return false;
+ }
+
+ public void restoreSearchBox() {
+ if (isSearchBoxEnabled()) {
+ mUiController.showSearchBox(mSearchBoxOnClickListener);
+ mShowingSearchBox = true;
+ }
}
private final CarMenuCallbacks.OnChildrenChangedListener mMenuListener =
@@ -262,71 +308,29 @@
@Override
public void onChildrenChanged(String parentId) {
if (mOnCreateCalled) {
- mBinder.onChildrenChanged(parentId);
+ mUiController.onChildrenChanged(parentId);
}
}
@Override
public void onChildChanged(String parentId, Bundle item,
- Drawable leftIcon, Drawable rightIcon) {
+ Drawable leftIcon, Drawable rightIcon) {
DisplayMetrics metrics = getResources().getDisplayMetrics();
if (leftIcon != null) {
- item.putParcelable(Constants.CarMenuConstants.KEY_LEFTICON,
+ item.putParcelable(MenuItemConstants.KEY_LEFTICON,
Utils.snapshot(metrics, leftIcon));
}
if (rightIcon != null) {
- item.putParcelable(Constants.CarMenuConstants.KEY_RIGHTICON,
+ item.putParcelable(MenuItemConstants.KEY_RIGHTICON,
Utils.snapshot(metrics, rightIcon));
}
if (mOnCreateCalled) {
- mBinder.onChildChanged(parentId, item);
+ mUiController.onChildChanged(parentId, item);
}
}
};
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- super.setContentView(mUiController.getContentView());
- mBinder = new CarMenuCallbacksBinder(this);
- mInputManager = getInputManager();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- registerCarMenuCallbacks(mBinder);
- if (mMenuCallbacks != null) {
- mMenuCallbacks.registerOnChildrenChangedListener(mMenuListener);
- }
- mOnCreateCalled = true;
- }
- });
- }
-
- protected void onDestroy() {
- super.onDestroy();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mMenuCallbacks != null) {
- mMenuCallbacks.unregisterOnChildrenChangedListener(mMenuListener);
- mMenuCallbacks = null;
- }
- }
- });
- }
-
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- mDrawerShowing = savedInstanceState.getBoolean(KEY_DRAWERSHOWING);
- mUiController.onRestoreInstanceState(savedInstanceState);
- }
-
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(KEY_DRAWERSHOWING, mDrawerShowing);
- mUiController.onSaveInstanceState(outState);
- }
-
public void closeDrawer() {
mUiController.closeDrawer();
}
@@ -339,6 +343,18 @@
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}.
*
@@ -356,6 +372,10 @@
mSearchBoxOnClickListener = listener;
}
+ public void showSearchBox() {
+ showSearchBox(mSearchBoxOnClickListener);
+ }
+
public void hideSearchBox() {
if (isShowingSearchBox()) {
stopInput();
@@ -363,12 +383,8 @@
mSearchBoxEnabled = false;
}
- public boolean isShowingSearchBox() {
- return mShowingSearchBox;
- }
-
public void setSearchBoxEditListener(SearchBoxEditListener listener) {
- mUiController.setSearchBoxEditListener(new SearchBoxEditListenerBinder(listener));
+ mUiController.setSearchBoxEditListener(listener);
}
public void stopInput() {
@@ -399,15 +415,16 @@
*/
public void startInput(final String hint, final View.OnClickListener onClickListener) {
mInputManager = getInputManager();
- EditText inputView = mUiController.startSearchInput(hint, onClickListener);
+ EditText inputView = mUiController.startInput(hint, onClickListener);
getInputManager().startInput(inputView);
mSearchBoxView = inputView;
mShowingSearchBox = true;
}
- public void setSearchBoxColors(int backgroundColor, int googleLogoColor, int textColor,
+ public void setSearchBoxColors(int backgroundColor, int searchLogoColor, int textColor,
int hintTextColor) {
- mUiController.setSearchBoxColors(backgroundColor, googleLogoColor, textColor, hintTextColor);
+ mUiController.setSearchBoxColors(backgroundColor, searchLogoColor,
+ textColor, hintTextColor);
}
public void setSearchBoxEndView(View endView) {
@@ -415,234 +432,6 @@
}
public void showToast(String text, int duration) {
- }
-
- private static class SearchBoxEditListenerBinder extends ISearchBoxEditListener.Stub {
- private final SearchBoxEditListener mListener;
-
- public SearchBoxEditListenerBinder(SearchBoxEditListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("Listener cannot be null");
- }
- mListener = listener;
- }
-
- @Override
- public void onSearch(String text) {
- mListener.onSearch(text);
- }
-
- @Override
- public void onEdit(String text) {
- mListener.onEdit(text);
- }
- }
-
- private static class CarMenuCallbacksBinder extends ICarMenuCallbacks.Stub {
- // Map of subscribed ids to their respective callbacks.
- private final Map<String, List<ISubscriptionCallbacks>> mSubscriptionMap = new HashMap<>();
- private OnMenuClickListener mMenuClickListener;
- private final WeakReference<CarDrawerActivity> mActivity;
-
- CarMenuCallbacksBinder(CarDrawerActivity activity) {
- mActivity = new WeakReference<>(activity);
- }
-
- @Override
- public int getVersion() {
- return 0;
- }
-
- @Override
- public Bundle getRoot() throws RemoteException {
- CarDrawerActivity activity = mActivity.get();
- if (activity != null && activity.mMenuCallbacks != null) {
- Root root = activity.mMenuCallbacks.onGetRoot(null);
- return root.getBundle();
- } else {
- return null;
- }
- }
-
- @Override
- public synchronized void subscribe(final String parentId,
- final ISubscriptionCallbacks callbacks) throws RemoteException {
- if (!mSubscriptionMap.containsKey(parentId)) {
- mSubscriptionMap.put(parentId, new ArrayList<ISubscriptionCallbacks>());
- }
- mSubscriptionMap.get(parentId).add(callbacks);
- loadResultsForClient(parentId, callbacks);
- }
-
- @Override
- public synchronized void unsubscribe(String id, ISubscriptionCallbacks callbacks)
- throws RemoteException {
- mSubscriptionMap.get(id).remove(callbacks);
- }
-
- @Override
- public void onCarMenuOpened() throws RemoteException {
- CarDrawerActivity activity = mActivity.get();
- if (activity != null) {
- activity.mDrawerShowing = true;
- activity.mMenuCallbacks.onCarMenuOpened();
- }
- }
-
-
- @Override
- public void onCarMenuClosing() throws RemoteException {
- CarDrawerActivity activity = mActivity.get();
- if (activity != null) {
- if (activity.mSearchBoxEnabled) {
- activity.mUiController.showSearchBox(activity.mSearchBoxOnClickListener);
- activity.mShowingSearchBox = true;
- }
- }
- }
-
- @Override
- public void onCarMenuClosed() throws RemoteException {
- CarDrawerActivity activity = mActivity.get();
- if (activity != null) {
- activity.mDrawerShowing = false;
- if (activity.mSearchBoxEnabled && !activity.mShowingSearchBox) {
- activity.showSearchBox(activity.mSearchBoxOnClickListener);
- }
- }
- }
-
- @Override
- public void onCarMenuOpening() throws RemoteException {
- CarDrawerActivity activity = mActivity.get();
- if (activity != null) {
- activity.stopInput();
- }
- }
-
- @Override
- public void onItemClicked(String id) throws RemoteException {
- CarDrawerActivity activity = mActivity.get();
- if (activity != null) {
- activity.mMenuCallbacks.onItemClicked(id);
- // TODO: Add support for IME
- activity.stopInput();
- }
- }
-
- @Override
- public boolean onItemLongClicked(String id) throws RemoteException {
- CarDrawerActivity activity = mActivity.get();
- if (activity != null) {
- return activity.mMenuCallbacks.onItemLongClicked(id);
- } else {
- return false;
- }
- }
-
- @Override
- public void onStateChanged(int newState) throws RemoteException {
- CarDrawerActivity activity = mActivity.get();
- if (activity != null) {
- activity.mMenuCallbacks.onStateChanged(newState);
- }
- }
-
- public void setOnMenuClickedListener(OnMenuClickListener listener) {
- mMenuClickListener = listener;
- }
-
- @Override
- public boolean onMenuClicked() {
- CarDrawerActivity activity = mActivity.get();
- if (activity == null) {
- return false;
- }
-
- if (mMenuClickListener != null) {
- if (mMenuClickListener.onClicked()) {
- return true;
- }
- }
- return false;
- }
-
- public void onChildrenChanged(String parentId) {
- if (mSubscriptionMap.containsKey(parentId)) {
- loadResultsForAllClients(parentId);
- }
- }
-
- public void reloadSubscribedMenus() {
- for (String parentId : mSubscriptionMap.keySet()) {
- loadResultsForAllClients(parentId);
- }
- }
-
- private void loadResultsForClient(final String parentId,
- final ISubscriptionCallbacks callbacks) {
- CarDrawerActivity activity = mActivity.get();
- if (activity == null) {
- return;
- }
-
- final CarMenu result = new CarMenu(activity.getResources().getDisplayMetrics()) {
- @Override
- protected void onResultReady(List<Bundle> list) {
- synchronized (CarMenuCallbacksBinder.this) {
- try {
- callbacks.onChildrenLoaded(parentId, list);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling onChildrenLoaded: ", e);
- }
- }
- }
- };
-
- activity.mMenuCallbacks.onLoadChildren(parentId, result);
- if (!result.isDone()) {
- throw new IllegalStateException("You must either call sendResult() or detach() " +
- "before returning!");
- }
- }
-
- private void loadResultsForAllClients(final String parentId) {
- CarDrawerActivity activity = mActivity.get();
- if (activity == null) {
- return;
- }
-
- final CarMenu result = new CarMenu(activity.getResources().getDisplayMetrics()) {
- @Override
- protected void onResultReady(List<Bundle> list) {
- synchronized (CarMenuCallbacksBinder.this) {
- if (mSubscriptionMap.containsKey(parentId)) {
- try {
- for (ISubscriptionCallbacks callbacks :
- mSubscriptionMap.get(parentId)) {
- callbacks.onChildrenLoaded(parentId, list);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling onChildrenLoaded: ", e);
- }
- }
- }
- }
- };
-
- activity.mMenuCallbacks.onLoadChildren(parentId, result);
- }
-
- private synchronized void onChildChanged(String parentId, Bundle item) {
- if (mSubscriptionMap.containsKey(parentId)) {
- for (ISubscriptionCallbacks callbacks : mSubscriptionMap.get(parentId)) {
- try {
- callbacks.onChildChanged(parentId, item);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling onChildChanged: ", e);
- }
- }
- }
- }
+ 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
index 2b7658c..aeb7d7c 100644
--- a/car-support-lib/src/android/support/car/app/menu/CarMenu.java
+++ b/car-support-lib/src/android/support/car/app/menu/CarMenu.java
@@ -19,13 +19,10 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.support.annotation.IntDef;
-import android.support.car.app.menu.Constants.CarMenuConstants;
+import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
import android.util.DisplayMetrics;
import android.widget.RemoteViews;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -34,39 +31,6 @@
* Use the {@link Builder} to populate the contents of the sublevel.
*/
public class CarMenu {
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {CarMenu.FLAG_BROWSABLE, CarMenu.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;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {WIDGET_CHECKBOX, WIDGET_TEXT_VIEW})
- public @interface WidgetTypes {}
-
- /**
- * Use a checkbox widget.
- * For use with {@link Builder#setWidget(int)}
- */
- public static final int WIDGET_CHECKBOX = 0x1;
-
- /**
- * Use a TextView widget
- * For use with {@link Builder#setWidget(int)}
- */
- public static final int WIDGET_TEXT_VIEW = 0x2;
-
private boolean mDetachCalled;
private boolean mSendResultCalled;
private final DisplayMetrics mMetrics;
@@ -88,11 +52,11 @@
for (Item item : results) {
ItemImpl impl = (ItemImpl) item;
if (impl.mIcon != null) {
- impl.mBundle.putParcelable(CarMenuConstants.KEY_LEFTICON, snapshot(impl.mIcon));
+ impl.mBundle.putParcelable(MenuItemConstants.KEY_LEFTICON, snapshot(impl.mIcon));
}
if (impl.mRightIcon != null) {
impl.mBundle.putParcelable(
- CarMenuConstants.KEY_RIGHTICON, snapshot(impl.mRightIcon));
+ MenuItemConstants.KEY_RIGHTICON, snapshot(impl.mRightIcon));
}
resultBundle.add(impl.mBundle);
}
@@ -165,7 +129,8 @@
/**
* Gets the integer constant for the widget.
*
- * @return Either {@link #WIDGET_CHECKBOX} or -1, if no widget was set.
+ * @return Either {@link MenuItemConstants.WidgetTypes#WIDGET_CHECKBOX} or -1,
+ * if no widget was set.
*/
int getWidget();
@@ -198,32 +163,32 @@
@Override
public String getId() {
- return mBundle.getString(CarMenuConstants.KEY_ID);
+ return mBundle.getString(MenuItemConstants.KEY_ID);
}
@Override
public String getTitle() {
- return mBundle.getString(CarMenuConstants.KEY_TITLE);
+ return mBundle.getString(MenuItemConstants.KEY_TITLE);
}
@Override
public String getText() {
- return mBundle.getString(CarMenuConstants.KEY_TEXT);
+ return mBundle.getString(MenuItemConstants.KEY_TEXT);
}
@Override
public int getWidget() {
- return mBundle.getInt(CarMenuConstants.KEY_WIDGET, -1);
+ return mBundle.getInt(MenuItemConstants.KEY_WIDGET, -1);
}
@Override
public boolean getWidgetState() {
- return mBundle.getBoolean(CarMenuConstants.KEY_WIDGET_STATE);
+ return mBundle.getBoolean(MenuItemConstants.KEY_WIDGET_STATE);
}
@Override
public int getFlags() {
- return mBundle.getInt(CarMenuConstants.KEY_FLAGS);
+ return mBundle.getInt(MenuItemConstants.KEY_FLAGS);
}
}
@@ -246,7 +211,7 @@
if (id == null) {
throw new IllegalStateException("Cannot pass a null id to the Builder.");
}
- mBundle.putString(CarMenuConstants.KEY_ID, id);
+ mBundle.putString(MenuItemConstants.KEY_ID, id);
}
/**
@@ -256,7 +221,7 @@
* @return This to chain calls
*/
public Builder setTitle(String title) {
- mBundle.putString(CarMenuConstants.KEY_TITLE, title);
+ mBundle.putString(MenuItemConstants.KEY_TITLE, title);
return this;
}
@@ -267,7 +232,7 @@
* @return This {@link Builder} to chain calls
*/
public Builder setText(String text) {
- mBundle.putString(CarMenuConstants.KEY_TEXT, text);
+ mBundle.putString(MenuItemConstants.KEY_TEXT, text);
return this;
}
@@ -278,7 +243,7 @@
* @return This {@link Builder} to chain calls
*/
public Builder setIcon(Bitmap bitmap) {
- mBundle.putParcelable(CarMenuConstants.KEY_LEFTICON, bitmap);
+ mBundle.putParcelable(MenuItemConstants.KEY_LEFTICON, bitmap);
return this;
}
@@ -304,7 +269,7 @@
* @return This {@link Builder} to chain calls
*/
public Builder setRightIcon(Bitmap bitmap) {
- mBundle.putParcelable(CarMenuConstants.KEY_RIGHTICON, bitmap);
+ mBundle.putParcelable(MenuItemConstants.KEY_RIGHTICON, bitmap);
return this;
}
@@ -332,7 +297,7 @@
* @return This {@link Builder} to chain calls
*/
public Builder setWidget(int widget) {
- mBundle.putInt(CarMenuConstants.KEY_WIDGET, widget);
+ mBundle.putInt(MenuItemConstants.KEY_WIDGET, widget);
return this;
}
@@ -345,7 +310,7 @@
* @return This {@link Builder} to chain calls
*/
public Builder setWidgetState(boolean on) {
- mBundle.putBoolean(CarMenuConstants.KEY_WIDGET_STATE, on);
+ mBundle.putBoolean(MenuItemConstants.KEY_WIDGET_STATE, on);
return this;
}
@@ -358,36 +323,36 @@
* @return This {@link Builder} to chain calls
*/
public Builder setIsEmptyPlaceHolder(boolean isEmptyPlaceHolder) {
- mBundle.putBoolean(CarMenuConstants.KEY_EMPTY_PLACEHOLDER, isEmptyPlaceHolder);
+ mBundle.putBoolean(MenuItemConstants.KEY_EMPTY_PLACEHOLDER, isEmptyPlaceHolder);
return this;
}
/**
- * If the widget is {@link #WIDGET_TEXT_VIEW}, then this will allow setting
+ * 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(CarMenuConstants.KEY_RIGHTTEXT, text);
+ mBundle.putString(MenuItemConstants.KEY_RIGHTTEXT, text);
return this;
}
public Builder setRemoteViews(RemoteViews views) {
- mBundle.putParcelable(CarMenuConstants.KEY_REMOTEVIEWS, views);
+ mBundle.putParcelable(MenuItemConstants.KEY_REMOTEVIEWS, views);
return this;
}
/**
* Sets additional flags for this item.
- * {@link #FLAG_BROWSABLE} is the only one that can be currently set
+ * {@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(@MenuItemFlags int flags) {
- mBundle.putInt(CarMenuConstants.KEY_FLAGS, flags);
+ public Builder setFlags(@MenuItemConstants.MenuItemFlags int flags) {
+ mBundle.putInt(MenuItemConstants.KEY_FLAGS, flags);
return this;
}
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
index b544690..5b92196 100644
--- a/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java
+++ b/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java
@@ -47,9 +47,9 @@
* 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 Root} which contains the root id and any hints
+ * @return The {@link RootMenu} which contains the root id and any hints
*/
- public abstract Root onGetRoot(Bundle hints);
+ public abstract RootMenu onGetRoot(Bundle hints);
/**
* Called when the CarMenu subscribes to to a certain id
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
index bcb8909..c3f45b2 100644
--- a/car-support-lib/src/android/support/car/app/menu/CarUiController.java
+++ b/car-support-lib/src/android/support/car/app/menu/CarUiController.java
@@ -16,259 +16,98 @@
package android.support.car.app.menu;
-import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
+import android.support.car.app.CarAppUtil;
import android.view.View;
-import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
+import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
/**
- * A controller for a {@link android.support.car.app.CarActivity} to access its car UI.
- *
- * @hide
+ * 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.
*/
-public class CarUiController {
- private static final String TAG = "CarUiController";
- // 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.support.car.ui.provider";
+public abstract class CarUiController {
+ static final String PROJECTED_UI_CONTROLLER =
+ "com.google.android.car.ProjectedCarUiController";
- private static final String PROJECTED_UI_ENTRY_CLASS_NAME = ".sdk.SdkEntry2";
- private static final String PROJECTED_UI_PROVIDER_PKG =
- "com.google.android.projection.gearhead";
-
- private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
-
- private final CarDrawerActivity mActivity;
- // TODO: Add more UI control methods
- private Method mGetContentViewMethod;
- private Method mSetScrimColor;
- private Method mSetTitle;
- private Method mSetCarMenuBinder;
- private Method mRestoreMenuButtonDrawable;
- private Method mSetMenuButtonBitmap;
- private Method mGetFragmentContainerId;
- private Method mSetLightMode;
- private Method mSetAutoLightDarkMode;
- private Method mSetDarkMode;
- private Method mSetBackground;
- private Method mSetBackgroundResource;
- private Method mOnSaveInstanceState;
- private Method mOnRestoreInstanceState;
- private Method mCloseDrawer;
- private Method mOpenDrawer;
- private Method mShowMenu;
- private Method mOnStart;
- private Method mOnResume;
- private Method mOnPause;
- private Method mOnStop;
- private Method mShowSearchBox;
- private Method mSetSearchBoxEditListener;
- private Method mStartInput;
- private Method mGetText;
- private Method mStopInput;
- private Method mStartSearchInput;
- private Method mSetSearchBoxEndView;
- private Method mSetSearchBoxColors;
-
- private Object mCarUiEntryClass;
+ protected final CarDrawerActivity mActivity;
public CarUiController(CarDrawerActivity activity) {
mActivity = activity;
- init();
+ validateCarUiPackage();
}
- public void init() {
- try{
- String className;
- String pkg;
- if (mActivity.getContext().getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
- className = UI_ENTRY_CLASS_NAME;
- pkg = CAR_UI_PROVIDER_PKG;
- } else {
- className = PROJECTED_UI_ENTRY_CLASS_NAME;
- pkg = PROJECTED_UI_PROVIDER_PKG;
- }
-
- // STOPSHIP: need to verify the certificate of the CarUiProvider apk.
- Context carUiContext = mActivity.getContext().createPackageContext(
- pkg,
- Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
- ClassLoader classLoader = carUiContext.getClassLoader();
- Class<?> loadedClass = classLoader.loadClass(pkg + className);
- for (Method m : loadedClass.getDeclaredMethods()) {
- switch(m.getName()) {
- case "getContentView":
- mGetContentViewMethod = m;
- break;
- case "setScrimColor":
- mSetScrimColor = m;
- break;
- case "setTitle":
- mSetTitle = m;
- break;
- case "setCarMenuBinder":
- mSetCarMenuBinder = m;
- break;
- case "restoreMenuDrawable":
- mRestoreMenuButtonDrawable = m;
- break;
- case "setMenuButtonBitmap":
- mSetMenuButtonBitmap = m;
- break;
- case "getFragmentContainerId":
- mGetFragmentContainerId = m;
- break;
- case "setLightMode":
- mSetLightMode = m;
- break;
- case "setDarkMode":
- mSetDarkMode = m;
- break;
- case "setAutoLightDarkMode":
- mSetAutoLightDarkMode = m;
- break;
- case "setBackground":
- mSetBackground = m;
- break;
- case "setBackgroundResource":
- mSetBackgroundResource = m;
- break;
- case "onSaveInstanceState":
- mOnSaveInstanceState = m;
- break;
- case "onRestoreInstanceState":
- mOnRestoreInstanceState = m;
- break;
- case "closeDrawer":
- mCloseDrawer = m;
- break;
- case "openDrawer":
- mOpenDrawer = m;
- break;
- case "showMenu":
- mShowMenu = m;
- break;
- case "onStart":
- mOnStart = m;
- break;
- case "onResume":
- mOnResume = m;
- break;
- case "onPause":
- mOnPause = m;
- break;
- case "onStop":
- mOnStop = m;
- break;
- case "showSearchBox":
- mShowSearchBox = m;
- break;
- case "setSearchBoxEditListener":
- mSetSearchBoxEditListener = m;
- break;
- case "startInput":
- mStartInput = m;
- break;
- case "getText":
- mGetText = m;
- break;
- case "stopInput":
- mStopInput = m;
- break;
- case "startSearchInput":
- mStartSearchInput = m;
- break;
- case "setSearchBoxEndView":
- mSetSearchBoxEndView = m;
- break;
- case "setSearchBoxColors":
- mSetSearchBoxColors = m;
- break;
- }
- }
- mCarUiEntryClass = loadedClass.getConstructors()[0].newInstance(
- carUiContext, mActivity.getContext());
- } catch (PackageManager.NameNotFoundException | ClassNotFoundException
- | InvocationTargetException | InstantiationException
- | IllegalAccessException e) {
- Log.e(TAG, "Unable to load Car ui entry class.", e);
+ public static CarUiController createCarUiController(CarDrawerActivity activity) {
+ if (CarAppUtil.isEmbeddedCar(activity.getContext())) {
+ return new EmbeddedCarUiController(activity);
+ } else {
+ return getProjectedCarUiController(PROJECTED_UI_CONTROLLER, activity);
}
}
- public int getFragmentContainerId() {
- return (int) invoke(mGetFragmentContainerId);
- }
-
- public void setTitle(CharSequence title) {
- invoke(mSetTitle, title);
- }
-
- public void setScrimColor(int color) {
- invoke(mSetScrimColor, color);
- }
-
- public View getContentView() {
- return (View) invoke(mGetContentViewMethod);
- }
-
- private Object invoke(Method m, Object... params) {
+ private static CarUiController getProjectedCarUiController(String className,
+ CarDrawerActivity activity) {
+ Class uiControllerClass = null;
try {
- return m.invoke(mCarUiEntryClass, params);
- } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
- Log.e(TAG, "Error invoking: " + m.getName(), e);
+ uiControllerClass = Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Cannot find ProjectedCarUiController:" +
+ className, e);
}
- return null;
+ Constructor<?> ctor;
+ try {
+ ctor = uiControllerClass.getDeclaredConstructor(CarDrawerActivity.class);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("Cannot construct ProjectedCarUiController," +
+ " no constructor: " + className, e);
+ }
+ try {
+ return (CarUiController) ctor.newInstance(activity);
+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException e) {
+ throw new IllegalArgumentException(
+ "Cannot construct ProjectedCarUiController, constructor failed for "
+ + uiControllerClass.getName(), e);
+ }
}
- public void registerCarMenuCallbacks(IBinder callbacks) {
- invoke(mSetCarMenuBinder, callbacks);
- }
+ public abstract void validateCarUiPackage();
- public void restoreMenuButtonDrawable() {
- invoke(mRestoreMenuButtonDrawable);
- }
+ public abstract int getFragmentContainerId();
- public void setMenuButtonBitmap(Bitmap bitmap) {
- invoke(mSetMenuButtonBitmap, bitmap);
- }
+ public abstract void setTitle(CharSequence title);
- /**
- * Set the System UI to be light.
- */
- public void setLightMode() {
- invoke(mSetLightMode);
- }
+ 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 void setDarkMode() {
- invoke(mSetDarkMode);
- }
+ public abstract void setDarkMode();
/**
* Set the System UI to be dark during day mode and light during night mode.
*/
- public void setAutoLightDarkMode() {
- invoke(mSetAutoLightDarkMode);
- }
+ public abstract void setAutoLightDarkMode();
/**
* Sets the application background to the given {@link android.graphics.Bitmap}.
*
* @param bitmap to use as background.
*/
- public void setBackground(Bitmap bitmap) {
- invoke(mSetBackground, bitmap);
- }
+ public abstract void setBackground(Bitmap bitmap);
/**
* Sets the background to a given resource.
@@ -276,77 +115,45 @@
*
* @param resId The identifier of the resource.
*/
- public void setBackgroundResource(int resId) {
- invoke(mSetBackgroundResource, resId);
- }
+ public abstract void setBackgroundResource(int resId);
- public void onRestoreInstanceState(Bundle savedState) {
- invoke(mOnRestoreInstanceState, savedState);
- }
+ public abstract void onRestoreInstanceState(Bundle savedState);
- public void onSaveInstanceState(Bundle outState) {
- invoke(mOnSaveInstanceState, outState);
- }
+ public abstract void onSaveInstanceState(Bundle outState);
- public void closeDrawer() {
- invoke(mCloseDrawer);
- }
+ public abstract void closeDrawer();
- public void openDrawer() {
- invoke(mOpenDrawer);
- }
+ public abstract void openDrawer();
- public void showMenu(String id, String title) {
- invoke(mShowMenu, id, title);
- }
+ public abstract void showMenu(String id, String title);
- public void onStart() {
- invoke(mOnStart);
- }
+ public abstract void onStart();
- public void onResume() {
- invoke(mOnResume);
- }
+ public abstract void onResume();
- public void onPause() {
- invoke(mOnPause);
- }
+ public abstract void onPause();
- public void onStop() {
- invoke(mOnStop);
- }
+ public abstract void onStop();
- public void showSearchBox(View.OnClickListener listener) {
- invoke(mShowSearchBox, listener);
- }
+ public abstract void showSearchBox(View.OnClickListener listener);
- public void setSearchBoxColors(int backgroundColor, int googleLogoColor, int textColor,
- int hintTextColor) {
- invoke(mSetSearchBoxColors, backgroundColor, googleLogoColor, textColor, hintTextColor);
- }
+ public abstract void setSearchBoxColors(int backgroundColor, int googleLogoColor, int textColor,
+ int hintTextColor);
- public void setSearchBoxEditListener(IBinder listener) {
- invoke(mSetSearchBoxEditListener, listener);
- }
+ public abstract void setSearchBoxEditListener(SearchBoxEditListener listener);
- public Object startInput(
- EditorInfo outAttrs, String hint, View.OnClickListener searchBoxClickListener) {
- return invoke(mStartInput, outAttrs, hint, searchBoxClickListener);
- }
+ public abstract EditText startInput(
+ String hint, View.OnClickListener searchBoxClickListener);
- public CharSequence getText() {
- return (CharSequence) invoke(mGetText);
- }
+ public abstract CharSequence getText();
- public void stopInput() {
- invoke(mStopInput);
- }
+ public abstract void stopInput();
- public EditText startSearchInput(String hint, View.OnClickListener listener) {
- return (EditText) invoke(mStartSearchInput, hint, listener);
- }
+ public abstract void setSearchBoxEndView(View view);
- public void setSearchBoxEndView(View view) {
- invoke(mSetSearchBoxEndView, 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/CarUiEntry.java b/car-support-lib/src/android/support/car/app/menu/CarUiEntry.java
deleted file mode 100644
index b042793..0000000
--- a/car-support-lib/src/android/support/car/app/menu/CarUiEntry.java
+++ /dev/null
@@ -1,98 +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.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.view.View;
-
-/**
- * A base class for a car ui entry which is used for loading and manipulating common car
- * app decor window (CarUi).
- *
- * A CarUi provider provides essential ui elements that a car app may want to use. The CarUi is
- * loaded by apps at runtime, similar to a shared library, but via reflection through a class that
- * extends {@link android.support.car.app.menu.CarUiEntry} from a separate apk
- * called CarUiProvider. Depending on the different platforms, the CarUiProvider may
- * be different and can be customized by different car makers. However, it is required that a
- * set of basic ui elements and functionalities exist in the CarUiProvider. This class defines
- * the set of must have functions in a CarUiProvider.
- */
-public abstract class CarUiEntry {
- protected Context mAppContext;
- protected Context mUiLibContext;
-
- public CarUiEntry(Context uiLibContext, Context appContext) {
- mAppContext = appContext;
- mUiLibContext = uiLibContext.createConfigurationContext(
- appContext.getResources().getConfiguration());
- }
-
- abstract public View getContentView();
-
- abstract public void setCarMenuBinder(IBinder binder) throws RemoteException;
-
- abstract public int getFragmentContainerId();
-
- abstract public void setBackground(Bitmap bitmap);
-
- abstract public void setBackgroundResource(int resId);
-
- abstract public void hideMenuButton();
-
- abstract public void restoreMenuDrawable();
-
- abstract public void setMenuProgress(float progress);
-
- abstract public void setScrimColor(int color);
-
- abstract public void setTitle(CharSequence title);
-
- abstract public void setTitleText(CharSequence title);
-
- abstract public void closeDrawer();
-
- abstract public void openDrawer();
-
- abstract public void showMenu(String id, String title);
-
- abstract public void setMenuButtonColor(int color);
-
- abstract public void showTitle();
-
- abstract public void hideTitle();
-
- abstract public void setLightMode();
-
- abstract public void setDarkMode();
-
- abstract public void setAutoLightDarkMode();
-
- abstract public void onRestoreInstanceState(Bundle savedInstanceState);
-
- abstract public void onSaveInstanceState(Bundle outState);
-
- abstract public void onStart();
-
- abstract public void onResume();
-
- abstract public void onPause();
-
- abstract public void onStop();
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/Constants.java b/car-support-lib/src/android/support/car/app/menu/Constants.java
deleted file mode 100644
index f37cace..0000000
--- a/car-support-lib/src/android/support/car/app/menu/Constants.java
+++ /dev/null
@@ -1,38 +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;
-
-/**
- * Constants shared by SDK and GearHead.
- *
- * @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/app/menu/EmbeddedCarUiController.java b/car-support-lib/src/android/support/car/app/menu/EmbeddedCarUiController.java
new file mode 100644
index 0000000..9e16204
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/menu/EmbeddedCarUiController.java
@@ -0,0 +1,241 @@
+/*
+ * 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!");
+ }
+
+ int signatureMatchResult =
+ packageManager.checkSignatures(CAR_SERVICE_PKG, CAR_UI_PROVIDER_PKG);
+ if (signatureMatchResult != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException("The signature of car ui provider does not match the" +
+ "signature of the car service!");
+ }
+ } 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 setBackgroundResource(int resId) {
+ mCarUiEntry.setBackgroundResource(resId);
+ }
+
+ @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);
+ }
+}
diff --git a/car-support-lib/src/android/support/car/app/menu/ICarMenuCallbacks.aidl b/car-support-lib/src/android/support/car/app/menu/ICarMenuCallbacks.aidl
deleted file mode 100644
index a3da39c..0000000
--- a/car-support-lib/src/android/support/car/app/menu/ICarMenuCallbacks.aidl
+++ /dev/null
@@ -1,36 +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.ISubscriptionCallbacks;
-
-/** @hide */
-interface ICarMenuCallbacks {
- int getVersion() = 0;
- Bundle getRoot() = 1;
- void subscribe(String parentId, ISubscriptionCallbacks callbacks) = 2;
- void unsubscribe(String parentId, ISubscriptionCallbacks callbacks) = 3;
- void onCarMenuOpened() = 4;
- void onCarMenuClosed() = 5;
- void onItemClicked(String id) = 6;
- boolean onItemLongClicked(String id) = 7;
- void onStateChanged(int newState) = 8;
- boolean onMenuClicked() = 9;
- void onCarMenuOpening() = 10;
- void onCarMenuClosing() = 11;
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/ISubscriptionCallbacks.aidl b/car-support-lib/src/android/support/car/app/menu/ISubscriptionCallbacks.aidl
deleted file mode 100644
index 772cc44..0000000
--- a/car-support-lib/src/android/support/car/app/menu/ISubscriptionCallbacks.aidl
+++ /dev/null
@@ -1,29 +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 java.util.List;
-
-/** @hide */
-interface ISubscriptionCallbacks {
- int getVersion() = 0;
- void onChildrenLoaded(String parentId, in List<Bundle> items) = 1;
- void onError(String id) = 2;
- void onChildChanged(String parentId, in Bundle bundle) = 3;
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/Root.java b/car-support-lib/src/android/support/car/app/menu/RootMenu.java
similarity index 79%
rename from car-support-lib/src/android/support/car/app/menu/Root.java
rename to car-support-lib/src/android/support/car/app/menu/RootMenu.java
index fbdf0e7..041f083 100644
--- a/car-support-lib/src/android/support/car/app/menu/Root.java
+++ b/car-support-lib/src/android/support/car/app/menu/RootMenu.java
@@ -17,13 +17,14 @@
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 Root is the main menu.
+ * 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.
*/
-public class Root {
+public class RootMenu {
private final Bundle mBundle;
/**
@@ -31,7 +32,7 @@
*
* @param id Root id
*/
- public Root(String id) {
+ public RootMenu(String id) {
this(id, null);
}
@@ -41,9 +42,9 @@
* @param id Root id
* @param extras Hints to pass along
*/
- public Root(String id, Bundle extras) {
+ public RootMenu(String id, Bundle extras) {
mBundle = new Bundle();
- mBundle.putString(Constants.CarMenuConstants.KEY_ID, id);
+ mBundle.putString(MenuItemConstants.KEY_ID, id);
if (extras != null) {
mBundle.putAll(extras);
}
@@ -55,7 +56,7 @@
* @return The root id
*/
public String getId() {
- return mBundle.getString(Constants.CarMenuConstants.KEY_ID);
+ return mBundle.getString(MenuItemConstants.KEY_ID);
}
/**
diff --git a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl b/car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java
similarity index 79%
copy from car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
copy to car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java
index b8b6c55..154249d 100644
--- a/car-support-lib/src/android/support/car/app/menu/ISearchBoxEditListener.aidl
+++ b/car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java
@@ -15,14 +15,17 @@
*/
package android.support.car.app.menu;
-interface ISearchBoxEditListener {
+/**
+ * A listener that listens the user input to the search box.
+ */
+public abstract class SearchBoxEditListener {
/**
* The user hit enter on the keyboard.
*/
- void onSearch(in String text) = 0;
+ public abstract void onSearch(String text);
/**
* The user changed the text in the search box with the keyboard.
*/
- void onEdit(in String text) = 1;
-}
+ public abstract void onEdit(String text);
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..2a91928
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/menu/compat/CarMenuConstantsComapt.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+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
new file mode 100644
index 0000000..bf51758
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedCarMenuCallbacksCompat.java
@@ -0,0 +1,181 @@
+/*
+ * 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;
+
+import javax.annotation.concurrent.GuardedBy;
+
+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!");
+ }
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..6508c2f
--- /dev/null
+++ b/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedSearchBoxEditListenerCompat.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+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/CarSensorEvent.java b/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
index e428a2f..92ca326 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
@@ -47,12 +47,12 @@
public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0;
/**
* Index in {@link #floatValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
- * sensor. This value is fuel level in coverable distance.
+ * sensor. This value is fuel level in coverable distance. The unit is Km.
*/
public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1;
/**
- * Index in {@link #byteValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
- * sensor. This value is set to 0 if fuel low level warning is on.
+ * Index in {@link #intValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
+ * sensor. This value is set to 1 if fuel low level warning is on.
*/
public static final int INDEX_FUEL_LOW_WARNING = 0;
@@ -373,10 +373,10 @@
public static class FuelLevelData {
public long timeStampNs;
- /** If unsupported by the car, this value is 0. */
+ /** Fuel level in %. If unsupported by the car, this value is -1. */
public int level;
- /** If unsupported by the car, this value is 0. */
- public int range;
+ /** Fuel as possible range in Km. If unsupported by the car, this value is -1. */
+ public float range;
/** If unsupported by the car, this value is false. */
public boolean lowFuelWarning;
}
@@ -395,8 +395,21 @@
data = new FuelLevelData();
}
data.timeStampNs = timeStampNs;
- data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
- data.range = (int) floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
+ if (floatValues == null) {
+ data.level = -1;
+ data.range = -1;
+ } else {
+ if (floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE] < 0) {
+ data.level = -1;
+ } else {
+ data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
+ }
+ if (floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE] < 0) {
+ data.range = -1;
+ } else {
+ data.range = floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
+ }
+ }
data.lowFuelWarning = intValues[0] == 1;
return data;
}
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
index 7949a46..ebffaf8 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
@@ -87,49 +87,36 @@
* {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
*/
public static final int SENSOR_TYPE_GEAR = 7;
- public static final int SENSOR_TYPE_RESERVED8 = 8;
/**
* Day/night sensor. Sensor data is intValues[0].
*/
- public static final int SENSOR_TYPE_NIGHT = 9;
+ public static final int SENSOR_TYPE_NIGHT = 8;
/**
* Sensor type for location. Sensor data passed in floatValues.
*/
- public static final int SENSOR_TYPE_LOCATION = 10;
+ public static final int SENSOR_TYPE_LOCATION = 9;
/**
* Represents the current driving status of car. Different user interaction should be used
* depending on the current driving status. Driving status is intValues[0].
*/
- public static final int SENSOR_TYPE_DRIVING_STATUS = 11;
+ public static final int SENSOR_TYPE_DRIVING_STATUS = 10;
/**
* Environment like temperature and pressure.
*/
- public static final int SENSOR_TYPE_ENVIRONMENT = 12;
+ public static final int SENSOR_TYPE_ENVIRONMENT = 11;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED13 = 13;
+ public static final int SENSOR_TYPE_ACCELEROMETER = 12;
/** @hide */
- public static final int SENSOR_TYPE_ACCELEROMETER = 14;
+ public static final int SENSOR_TYPE_GPS_SATELLITE = 13;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED15 = 15;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED16 = 16;
- /** @hide */
- public static final int SENSOR_TYPE_GPS_SATELLITE = 17;
- /** @hide */
- public static final int SENSOR_TYPE_GYROSCOPE = 18;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED19 = 19;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED20 = 20;
- /** @hide */
- public static final int SENSOR_TYPE_RESERVED21 = 21;
+ public static final int SENSOR_TYPE_GYROSCOPE = 14;
/**
* Sensor type bigger than this is invalid. Always update this after adding a new sensor.
* @hide
*/
- private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_RESERVED21;
+ private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_GYROSCOPE;
/**
* Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
diff --git a/car-support-lib/src/android/support/car/media/CarAudioManager.java b/car-support-lib/src/android/support/car/media/CarAudioManager.java
index 3b1ba21..a8ab19b 100644
--- a/car-support-lib/src/android/support/car/media/CarAudioManager.java
+++ b/car-support-lib/src/android/support/car/media/CarAudioManager.java
@@ -16,6 +16,7 @@
package android.support.car.media;
import android.media.AudioAttributes;
+import android.media.AudioManager.OnAudioFocusChangeListener;
import android.os.RemoteException;
import android.support.annotation.IntDef;
import android.support.car.CarManagerBase;
@@ -86,4 +87,24 @@
* @return
*/
public abstract AudioAttributes getAudioAttributesForCarUsage(@CarAudioUsage int carUsage);
+
+ /**
+ * Request audio focus.
+ * Send a request to obtain the audio focus.
+ * @param l
+ * @param requestAttributes
+ * @param durationHint
+ * @param flags
+ */
+ public abstract int requestAudioFocus(OnAudioFocusChangeListener l,
+ AudioAttributes requestAttributes,
+ int durationHint,
+ int flags) throws IllegalArgumentException;
+ /**
+ * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
+ * @param l
+ * @param aa
+ * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
+ */
+ public abstract int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa);
}
diff --git a/car-support-lib/src/android/support/car/media/CarAudioManagerEmbedded.java b/car-support-lib/src/android/support/car/media/CarAudioManagerEmbedded.java
index 4fdac71..e254a93 100644
--- a/car-support-lib/src/android/support/car/media/CarAudioManagerEmbedded.java
+++ b/car-support-lib/src/android/support/car/media/CarAudioManagerEmbedded.java
@@ -16,6 +16,7 @@
package android.support.car.media;
import android.media.AudioAttributes;
+import android.media.AudioManager.OnAudioFocusChangeListener;
/**
* @hide
@@ -34,6 +35,19 @@
}
@Override
+ public int requestAudioFocus(OnAudioFocusChangeListener l,
+ AudioAttributes requestAttributes,
+ int durationHint,
+ int flags) throws IllegalArgumentException {
+ return mManager.requestAudioFocus(l, requestAttributes, durationHint, flags);
+ }
+
+ @Override
+ public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
+ return mManager.abandonAudioFocus(l, aa);
+ }
+
+ @Override
public void onCarDisconnected() {
//nothing to do
}
diff --git a/car-systemtest-lib/src/android/car/test/CarTestManager.java b/car-systemtest-lib/src/android/car/test/CarTestManager.java
index 89f55ad..cfd6310 100644
--- a/car-systemtest-lib/src/android/car/test/CarTestManager.java
+++ b/car-systemtest-lib/src/android/car/test/CarTestManager.java
@@ -66,6 +66,10 @@
// should not happen for embedded
}
+ /**
+ * Inject vehicle prop value event.
+ * @param value
+ */
public void injectEvent(VehiclePropValue value) {
try {
mService.injectEvent(new VehiclePropValueParcelable(value));
@@ -75,6 +79,19 @@
}
/**
+ * Check if given property is supported by vehicle hal.
+ * @param property
+ * @return
+ */
+ public boolean isPropertySupported(int property) {
+ try {
+ return mService.isPropertySupported(property);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ }
+ return false;
+ }
+ /**
* Start mocking vehicle HAL. It is somewhat strange to re-use interface in lower level
* API, but this is only for testing, and interface is exactly the same.
* @param mock
@@ -93,6 +110,9 @@
}
}
+ /**
+ * Stop previously started mocking.
+ */
public void stopMocking() {
IVehicleNetworkHalMockImpl halMockImpl;
synchronized (this) {
diff --git a/car-systemtest-lib/src/android/car/test/ICarTest.aidl b/car-systemtest-lib/src/android/car/test/ICarTest.aidl
index 2e763b7..05a2adc 100644
--- a/car-systemtest-lib/src/android/car/test/ICarTest.aidl
+++ b/car-systemtest-lib/src/android/car/test/ICarTest.aidl
@@ -27,4 +27,6 @@
void startMocking(in IVehicleNetworkHalMock mock, int flags) = 2;
/** Finish mocking mode. */
void stopMocking(in IVehicleNetworkHalMock mock) = 3;
+ /** If given property is supported or not */
+ boolean isPropertySupported(int property) = 4;
}
diff --git a/car-ui-provider/Android.mk b/car-ui-provider/Android.mk
index 6b9960e..a64c485 100644
--- a/car-ui-provider/Android.mk
+++ b/car-ui-provider/Android.mk
@@ -20,7 +20,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_CERTIFICATE := shared
+LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/car-ui-provider/AndroidManifest.xml b/car-ui-provider/AndroidManifest.xml
index dfd64d8..ffbdfed 100644
--- a/car-ui-provider/AndroidManifest.xml
+++ b/car-ui-provider/AndroidManifest.xml
@@ -16,9 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.support.car.ui.provider" >
- <uses-sdk android:minSdkVersion="22"
- android:targetSdkVersion="22" />
- <application android:label="@string/app_name"
- ></application>
+ package="android.car.ui.provider" >
+ <uses-sdk android:minSdkVersion="23"
+ android:targetSdkVersion="23" />
+ <application android:label="@string/app_name" />
</manifest>
diff --git a/car-ui-provider/res/layout/car_activity.xml b/car-ui-provider/res/layout/car_activity.xml
index 75cb267..776b1f5 100644
--- a/car-ui-provider/res/layout/car_activity.xml
+++ b/car-ui-provider/res/layout/car_activity.xml
@@ -24,7 +24,7 @@
android:layout_height="match_parent"
android:scaleType="centerCrop" />
- <android.support.car.ui.provider.CarDrawerLayout
+ <android.car.ui.provider.CarDrawerLayout
android:id="@+id/drawer_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
@@ -43,7 +43,7 @@
android:background="@color/car_card"
android:paddingTop="@dimen/car_drawer_header_height" >
- <android.support.car.ui.provider.PagedListView
+ <android.car.ui.provider.PagedListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -81,7 +81,7 @@
</android.support.v7.widget.CardView>
</FrameLayout>
- </android.support.car.ui.provider.CarDrawerLayout>
+ </android.car.ui.provider.CarDrawerLayout>
<LinearLayout
android:layout_width="match_parent"
diff --git a/car-ui-provider/res/layout/car_paged_recycler_view.xml b/car-ui-provider/res/layout/car_paged_recycler_view.xml
index c9c15ac..a01bf70 100644
--- a/car-ui-provider/res/layout/car_paged_recycler_view.xml
+++ b/car-ui-provider/res/layout/car_paged_recycler_view.xml
@@ -1,11 +1,25 @@
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <android.support.car.ui.provider.PagedScrollBarView
+ <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"
@@ -13,7 +27,7 @@
android:paddingTop="16dp"
android:visibility="invisible" />
- <android.support.car.ui.provider.MaxWidthLayout
+ <android.car.ui.provider.MaxWidthLayout
android:id="@+id/max_width_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -21,12 +35,12 @@
android:clipChildren="false"
app:carMaxWidth="@dimen/car_card_max_width" >
- <android.support.car.ui.provider.CarRecyclerView
+ <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.support.car.ui.provider.MaxWidthLayout>
+ </android.car.ui.provider.MaxWidthLayout>
</merge>
diff --git a/car-ui-provider/src/android/support/car/ui/provider/CarDrawerLayout.java b/car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java
similarity index 99%
rename from car-ui-provider/src/android/support/car/ui/provider/CarDrawerLayout.java
rename to car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java
index 9aabb14..cd3e01d 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/CarDrawerLayout.java
+++ b/car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.ui.provider;
+package android.car.ui.provider;
import android.content.Context;
import android.content.res.Resources;
diff --git a/car-ui-provider/src/android/support/car/ui/provider/CarRecyclerView.java b/car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java
similarity index 96%
rename from car-ui-provider/src/android/support/car/ui/provider/CarRecyclerView.java
rename to car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java
index 33fbcd3..04fcd63 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/CarRecyclerView.java
+++ b/car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.ui.provider;
+package android.car.ui.provider;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/car-ui-provider/src/android/support/car/ui/provider/CarUiEntry.java b/car-ui-provider/src/android/car/ui/provider/CarUiEntry.java
similarity index 90%
rename from car-ui-provider/src/android/support/car/ui/provider/CarUiEntry.java
rename to car-ui-provider/src/android/car/ui/provider/CarUiEntry.java
index 7361d79..4375c8b 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/CarUiEntry.java
+++ b/car-ui-provider/src/android/car/ui/provider/CarUiEntry.java
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.ui.provider;
+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;
@@ -22,10 +25,6 @@
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.support.car.app.menu.CarDrawerActivity;
-import android.support.car.app.menu.Constants;
import android.support.car.input.CarRestrictedEditText;
import android.support.car.ui.DrawerArrowDrawable;
import android.support.car.ui.PagedListView;
@@ -45,10 +44,7 @@
import android.support.car.ui.R;
-import android.support.car.app.menu.ICarMenuCallbacks;
-import android.support.car.app.menu.ISearchBoxEditListener;
-
-public class CarUiEntry extends android.support.car.app.menu.CarUiEntry {
+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
@@ -79,7 +75,7 @@
private ImageView mSearchBoxSuperSearchLogo;
private FrameLayout mSearchBoxEndView;
private View mTitleContainer;
- private CarDrawerActivity.SearchBoxEditListener mSearchBoxEditListener;
+ private SearchBoxEditListener mSearchBoxEditListener;
public interface SearchBoxClickListener {
/**
@@ -171,12 +167,11 @@
};
@Override
- public void setCarMenuBinder(IBinder binder) throws RemoteException {
- ICarMenuCallbacks callbacks = ICarMenuCallbacks.Stub.asInterface(binder);
- Bundle root = callbacks.getRoot();
- if (root != null) {
+ public void setCarMenuCallbacks(CarMenuCallbacks callbacks){
+ RootMenu rootMenu = callbacks.getRootMenu(null);
+ if (rootMenu != null) {
mDrawerController.setRootAndCallbacks(
- root.getString(Constants.CarMenuConstants.KEY_ID), callbacks);
+ rootMenu.getId(), callbacks);
mDrawerController.setDrawerEnabled(true);
} else {
hideMenuButton();
@@ -215,18 +210,6 @@
mMenuButton.setImageDrawable(new BitmapDrawable(mUiLibContext.getResources(), bitmap));
}
- /**
- * 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
- */
- @Override
- public void setMenuProgress(float progress) {
- mDrawerArrowDrawable.setProgress(progress);
- }
-
@Override
public void setScrimColor(int color) {
mDrawerLayout.setScrimColor(color);
@@ -238,11 +221,6 @@
}
@Override
- public void setTitleText(CharSequence title) {
- mTitleView.setText(title);
- }
-
- @Override
public void closeDrawer() {
mDrawerController.closeDrawer();
}
@@ -257,40 +235,6 @@
mDrawerController.showMenu(id, title);
}
- 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));
- }
- 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);
- }
- };
@Override
public void setMenuButtonColor(int color) {
@@ -323,27 +267,64 @@
mDrawerController.setAutoLightDarkMode();
}
+ @Override
+ public void showToast(String msg, long duration) {
+ // TODO: add toast support
+ }
- public CharSequence getText() {
+ @Override
+ public CharSequence getSearchBoxText() {
return mCarRestrictedEditText.getText();
}
- public EditText startSearchInput(String hint,
+ @Override
+ public EditText startInput(String hint,
View.OnClickListener searchBoxClickListener) {
mSearchBoxClickListener = wrapSearchBoxClickListener(searchBoxClickListener);
setSearchBoxModeLarge(hint);
return mCarRestrictedEditText;
}
- public void setSearchBoxModeLarge(String hint) {
- mCarRestrictedEditText.setHint(hint);
- setSearchBoxMode(SEARCH_BOX_MODE_LARGE);
+
+ @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
@@ -354,11 +335,59 @@
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.
*/
@@ -439,20 +468,6 @@
}
}
- /**
- * Sets the view to be displayed at the end of the search box, or null to clear any existing
- * views.
- */
- 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);
- }
- }
private final Runnable mSetEditTextGoneRunnable = new Runnable() {
@Override
@@ -468,9 +483,6 @@
}
};
- public void stopInput() {
- setSearchBoxMode(SEARCH_BOX_MODE_NONE);
- }
private SearchBoxClickListener wrapSearchBoxClickListener(final View.OnClickListener listener) {
return new SearchBoxClickListener() {
@@ -481,71 +493,6 @@
};
}
- public void setSearchBoxEditListener(IBinder binder) {
- ISearchBoxEditListener listener = ISearchBoxEditListener.Stub.asInterface(binder);
- mSearchBoxEditListener = new WrappedSearchBoxEditListener(listener);
- }
-
- private static class WrappedSearchBoxEditListener
- implements CarDrawerActivity.SearchBoxEditListener {
- private final ISearchBoxEditListener mListener;
-
- public WrappedSearchBoxEditListener(ISearchBoxEditListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onSearch(String text) {
- try {
- mListener.onSearch(text);
- } catch (RemoteException e) {
- Log.d(TAG, "Error calling onSearch", e);
- }
- }
-
- @Override
- public void onEdit(String text) {
- try {
- mListener.onEdit(text);
- } catch (RemoteException e) {
- Log.d(TAG, "Error calling onEdit", e);
- }
- }
- }
-
- @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() {
-
- }
private static void setViewColor(View view, int color) {
if (view instanceof TextView) {
@@ -561,4 +508,39 @@
}
}
}
+
+ 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));
+ }
+ 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/support/car/ui/provider/DrawerApiAdapter.java b/car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java
similarity index 92%
rename from car-ui-provider/src/android/support/car/ui/provider/DrawerApiAdapter.java
rename to car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java
index b3f8ebb..2571187 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/DrawerApiAdapter.java
+++ b/car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.ui.provider;
+package android.car.ui.provider;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -40,17 +40,21 @@
import java.util.List;
import java.util.Map;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_EMPTY_PLACEHOLDER;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_FLAGS;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_ID;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_LEFTICON;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_REMOTEVIEWS;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_RIGHTICON;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_RIGHTTEXT;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_TEXT;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_TITLE;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_WIDGET;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_WIDGET_STATE;
+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 {
@@ -100,7 +104,7 @@
}
int flags = item.getInt(KEY_FLAGS);
- if ((flags & CarMenu.FLAG_BROWSABLE) != 0 || item.containsKey(KEY_RIGHTICON)) {
+ if ((flags & FLAG_BROWSABLE) != 0 || item.containsKey(KEY_RIGHTICON)) {
return R.layout.car_imageview;
}
@@ -109,9 +113,9 @@
}
switch (item.getInt(KEY_WIDGET)) {
- case CarMenu.WIDGET_CHECKBOX:
+ case WIDGET_CHECKBOX:
return R.layout.car_menu_checkbox;
- case CarMenu.WIDGET_TEXT_VIEW:
+ case WIDGET_TEXT_VIEW:
return R.layout.car_textview;
default:
return 0;
@@ -216,7 +220,7 @@
mNoLeftIcon = false;
}
if (bundle.containsKey(KEY_FLAGS) &&
- (bundle.getInt(KEY_FLAGS) & CarMenu.FLAG_FIRSTITEM) != 0) {
+ (bundle.getInt(KEY_FLAGS) & FLAG_FIRSTITEM) != 0) {
mFirstItemIndex = index;
}
mIdToPosMap.put(bundle.getString(KEY_ID), index);
@@ -332,7 +336,7 @@
return;
}
final int flags = item.getInt(KEY_FLAGS);
- if ((flags & CarMenu.FLAG_BROWSABLE) != 0) {
+ 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);
diff --git a/car-ui-provider/src/android/support/car/ui/provider/DrawerController.java b/car-ui-provider/src/android/car/ui/provider/DrawerController.java
similarity index 81%
rename from car-ui-provider/src/android/support/car/ui/provider/DrawerController.java
rename to car-ui-provider/src/android/car/ui/provider/DrawerController.java
index 07a9dab..d554f08 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/DrawerController.java
+++ b/car-ui-provider/src/android/car/ui/provider/DrawerController.java
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.ui.provider;
+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.os.RemoteException;
import android.support.car.ui.PagedListView;
import android.support.car.ui.R;
import android.support.v7.widget.CardView;
@@ -30,10 +30,6 @@
import android.view.animation.AnimationUtils;
import android.widget.ProgressBar;
-import android.support.car.app.menu.CarMenu;
-import android.support.car.app.menu.ICarMenuCallbacks;
-import android.support.car.app.menu.ISubscriptionCallbacks;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
@@ -41,9 +37,10 @@
import java.util.Queue;
import java.util.Stack;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_FLAGS;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_ID;
-import static android.support.car.app.menu.Constants.CarMenuConstants.KEY_TITLE;
+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
@@ -87,7 +84,7 @@
private final CardView mTruncatedListCardView;
private final Stack<Integer> mClickCountStack = new Stack<>();
- private ICarMenuCallbacks mCarMenuCallbacks;
+ private CarMenuCallbacks mCarMenuCallbacks;
private DrawerApiAdapter mAdapter;
private int mScrimColor = CarDrawerLayout.DEFAULT_SCRIM_COLOR;
private boolean mIsDrawerOpen;
@@ -128,11 +125,7 @@
mUiEntry.setMenuProgress(1.0f);
// This can be null on day/night mode changes
if (mCarMenuCallbacks != null) {
- try {
- mCarMenuCallbacks.onCarMenuOpened();
- } catch (RemoteException e) {
- Log.e(TAG, "Exception calling back on drawer opened: ", e);
- }
+ mCarMenuCallbacks.onCarMenuOpened();
}
}
@@ -145,11 +138,7 @@
mUiEntry.setTitle(mContentTitle);
// This can be null on day/night mode changes
if (mCarMenuCallbacks != null) {
- try {
- mCarMenuCallbacks.onCarMenuClosed();
- } catch (RemoteException e) {
- Log.e(TAG, "Exception calling back on drawer closed: ", e);
- }
+ mCarMenuCallbacks.onCarMenuClosed();
}
}
@@ -162,11 +151,7 @@
mIsDrawerAnimating = true;
// This can be null on day/night mode changes
if (mCarMenuCallbacks != null) {
- try {
- mCarMenuCallbacks.onCarMenuOpening();
- } catch (RemoteException e) {
- Log.e(TAG, "Exception calling back on drawer opening: ", e);
- }
+ mCarMenuCallbacks.onCarMenuOpening();
}
}
@@ -180,11 +165,7 @@
mIsDrawerAnimating = true;
// This can be null on day/night mode changes
if (mCarMenuCallbacks != null) {
- try {
- mCarMenuCallbacks.onCarMenuClosing();
- } catch (RemoteException e) {
- Log.e(TAG, "Exception calling back on drawer closing: ", e);
- }
+ mCarMenuCallbacks.onCarMenuClosing();
}
}
@@ -196,47 +177,39 @@
}
int flags = item.getInt(KEY_FLAGS);
String id = item.getString(KEY_ID);
- try {
- // 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 & CarMenu.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();
+
+ // 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;
}
- } catch (RemoteException e) {
- Log.e(TAG, "Exception: ", e);
+ 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) {
- try {
- return mCarMenuCallbacks.onItemLongClicked(item.getString(KEY_ID));
- } catch (RemoteException e) {
- Log.e(TAG, "Exception: ", e);
- }
- return false;
+ return mCarMenuCallbacks.onItemLongClicked(item.getString(KEY_ID));
}
@Override
@@ -261,7 +234,7 @@
}
}
- public void setRootAndCallbacks(String rootId, ICarMenuCallbacks callbacks) {
+ public void setRootAndCallbacks(String rootId, CarMenuCallbacks callbacks) {
mAdapter = new DrawerApiAdapter();
mAdapter.setItemSelectedListener(this);
mListView.setAdapter(mAdapter);
@@ -271,13 +244,8 @@
if (mSubscriptionIds.isEmpty()) {
setRootId(rootId);
} else {
- try {
- subscribe(mSubscriptionIds.peek());
- openDrawer();
- } catch (RemoteException e) {
- Log.e(TAG, "Error restoring drawer subscription state.", e);
- return;
- }
+ subscribe(mSubscriptionIds.peek());
+ openDrawer();
}
}
@@ -381,7 +349,7 @@
Bundle bundle = new Bundle();
bundle.putString(KEY_ID, id);
bundle.putString(KEY_TITLE, title);
- bundle.putInt(KEY_FLAGS, CarMenu.FLAG_BROWSABLE);
+ bundle.putInt(KEY_FLAGS, FLAG_BROWSABLE);
onItemClicked(bundle, 0 /* position */);
}
@@ -399,11 +367,7 @@
private void clearMenu() {
if (!mSubscriptionIds.isEmpty()) {
- try {
- mCarMenuCallbacks.unsubscribe(mSubscriptionIds.peek(), mSubscriptionCallbacks);
- } catch (RemoteException e) {
- Log.e(TAG, "Error unsubscribing from " + mSubscriptionIds.peek() + ": ", e);
- }
+ mCarMenuCallbacks.unsubscribe(mSubscriptionIds.peek(), mSubscriptionCallbacks);
mSubscriptionIds.clear();
mTitles.clear();
}
@@ -422,7 +386,7 @@
mContainer.addViewFader(mStatusViewViewFader);
}
- private void subscribe(String id) throws RemoteException {
+ private void subscribe(String id) {
mProgressBar.setVisibility(View.VISIBLE);
mCarMenuCallbacks.subscribe(id, mSubscriptionCallbacks);
}
@@ -438,16 +402,9 @@
if (mSubscriptionIds.size() > 1) {
mPlvAnimationController.enqueueBackAnimation(mClearAdapterRunnable);
mProgressBar.setVisibility(View.VISIBLE);
- try {
- mCarMenuCallbacks.unsubscribe(mSubscriptionIds.pop(),
- mSubscriptionCallbacks);
- subscribe(mSubscriptionIds.peek());
- } catch (RemoteException e) {
- Log.e(TAG, "Error subscribing or unsubscribing: ", e);
- mProgressBar.setVisibility(View.GONE);
- closeDrawer();
- return;
- }
+ mCarMenuCallbacks.unsubscribe(mSubscriptionIds.pop(),
+ mSubscriptionCallbacks);
+ subscribe(mSubscriptionIds.peek());
// Restore the title for this menu level.
mTitles.pop();
CharSequence title = mTitles.peek();
@@ -463,12 +420,8 @@
private final View.OnClickListener mMenuClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
- try {
- if (mIsDrawerAnimating || mCarMenuCallbacks.onMenuClicked()) {
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling mCarMenuCallbacks.onMenuClicked()");
+ if (mIsDrawerAnimating || mCarMenuCallbacks.onMenuClicked()) {
+ return;
}
// Check if drawer has root set.
if (mRootId == null) {
@@ -482,14 +435,9 @@
mIsCapped = false;
backOrClose();
} else {
- try {
- mSubscriptionIds.push(mRootId);
- mTitles.push(mContentTitle);
- subscribe(mRootId);
- } catch (RemoteException e) {
- Log.e(TAG, "Error subscribing to the root id " + mRootId, e);
- return;
- }
+ mSubscriptionIds.push(mRootId);
+ mTitles.push(mContentTitle);
+ subscribe(mRootId);
openDrawer();
}
}
@@ -639,18 +587,12 @@
}
}
- private class SubscriptionCallbacks extends ISubscriptionCallbacks.Stub {
+ private class SubscriptionCallbacks extends android.car.app.menu.SubscriptionCallbacks {
private final Object mItemLock = new Object();
private volatile List<Bundle> mItems;
@Override
- public int getVersion() {
- return 0;
- }
-
- @Override
- public void onChildrenLoaded(String parentId, final List<Bundle> items)
- throws RemoteException {
+ 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) {
@@ -676,12 +618,12 @@
}
@Override
- public void onError(String id) throws RemoteException {
+ public void onError(String id) {
// TODO: do something useful here.
}
@Override
- public void onChildChanged(String parentId, Bundle bundle) throws RemoteException {
+ 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.
diff --git a/car-ui-provider/src/android/support/car/ui/provider/MaxWidthLayout.java b/car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java
similarity index 96%
rename from car-ui-provider/src/android/support/car/ui/provider/MaxWidthLayout.java
rename to car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java
index f1547f4..4311c5a 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/MaxWidthLayout.java
+++ b/car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.ui.provider;
+package android.car.ui.provider;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/car-ui-provider/src/android/support/car/ui/provider/PagedListView.java b/car-ui-provider/src/android/car/ui/provider/PagedListView.java
similarity index 96%
rename from car-ui-provider/src/android/support/car/ui/provider/PagedListView.java
rename to car-ui-provider/src/android/car/ui/provider/PagedListView.java
index 3d0b1e7..9648dd6 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/PagedListView.java
+++ b/car-ui-provider/src/android/car/ui/provider/PagedListView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.ui.provider;
+package android.car.ui.provider;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/car-ui-provider/src/android/support/car/ui/provider/PagedScrollBarView.java b/car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java
similarity index 96%
rename from car-ui-provider/src/android/support/car/ui/provider/PagedScrollBarView.java
rename to car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java
index b4b6bbd..91a25ab 100644
--- a/car-ui-provider/src/android/support/car/ui/provider/PagedScrollBarView.java
+++ b/car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.support.car.ui.provider;
+package android.car.ui.provider;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
index 5925e9b..331f93c 100644
--- a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
+++ b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
@@ -707,8 +707,10 @@
}
if (!isCustom && v.getValueType() != valueType) {
- throw new IllegalArgumentException("Unexpected type: " + v.getValueType()
- + ", expecting: " + valueType);
+ throw new IllegalArgumentException(
+ "Unexpected type for property 0x" + Integer.toHexString(property) +
+ " got:0x" + Integer.toHexString(v.getValueType())
+ + ", expecting:0x" + Integer.toHexString(valueType));
}
return v;
}
diff --git a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
index faf56f6..52affef 100644
--- a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
+++ b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
@@ -15,7 +15,8 @@
* limitations under the License.
*/
-//Autogenerated from vehicle.h. Do not modify manually.
+//Autogenerated from vehicle.h using libvehiclenetwork/tool/vehicle_code_gen.py.
+//Do not modify manually.
package com.android.car.vehiclenetwork;
@@ -56,11 +57,11 @@
public static final int VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT = 0x00000902;
public static final int VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY = 0x00000903;
public static final int VEHICLE_PROPERTY_AUDIO_HW_VARIANT = 0x00000904;
-public static final int VEHICLE_PROPERTY_AUDIO_CONTEXT = 0x00000905;
public static final int VEHICLE_PROPERTY_AP_POWER_STATE = 0x00000A00;
public static final int VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS = 0x00000A01;
public static final int VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON = 0x00000A02;
public static final int VEHICLE_PROPERTY_HW_KEY_INPUT = 0x00000A10;
+public static final int VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO = 0x00000A20;
public static final int VEHICLE_PROPERTY_CUSTOM_START = 0x70000000;
public static final int VEHICLE_PROPERTY_CUSTOM_END = 0x73ffffff;
public static final int VEHICLE_PROPERTY_INTERNAL_START = 0x74000000;
@@ -97,16 +98,16 @@
case VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE: return VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT;
case VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE: return VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT;
case VEHICLE_PROPERTY_RADIO_PRESET: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
-case VEHICLE_PROPERTY_AUDIO_FOCUS: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC3;
+case VEHICLE_PROPERTY_AUDIO_FOCUS: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
case VEHICLE_PROPERTY_AUDIO_VOLUME: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC3;
case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
-case VEHICLE_PROPERTY_AUDIO_CONTEXT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
case VEHICLE_PROPERTY_AP_POWER_STATE: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
case VEHICLE_PROPERTY_HW_KEY_INPUT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
+case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
default: return VehicleValueType.VEHICLE_VALUE_TYPE_SHOUD_NOT_USE;
}
@@ -148,11 +149,11 @@
case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return "VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT";
case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return "VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY";
case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return "VEHICLE_PROPERTY_AUDIO_HW_VARIANT";
-case VEHICLE_PROPERTY_AUDIO_CONTEXT: return "VEHICLE_PROPERTY_AUDIO_CONTEXT";
case VEHICLE_PROPERTY_AP_POWER_STATE: return "VEHICLE_PROPERTY_AP_POWER_STATE";
case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return "VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS";
case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return "VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON";
case VEHICLE_PROPERTY_HW_KEY_INPUT: return "VEHICLE_PROPERTY_HW_KEY_INPUT";
+case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return "VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO";
case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return "VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE";
default: return "UNKNOWN_PROPERTY";
}
@@ -194,11 +195,11 @@
case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC };
-case VEHICLE_PROPERTY_AUDIO_CONTEXT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_AP_POWER_STATE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC };
case VEHICLE_PROPERTY_HW_KEY_INPUT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
default: return null;
}
@@ -240,11 +241,11 @@
case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
-case VEHICLE_PROPERTY_AUDIO_CONTEXT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
case VEHICLE_PROPERTY_AP_POWER_STATE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ , VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
case VEHICLE_PROPERTY_HW_KEY_INPUT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
+case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
default: return null;
}
@@ -362,11 +363,45 @@
public static final int VEHICLE_AUDIO_FOCUS_INDEX_FOCUS = 0;
public static final int VEHICLE_AUDIO_FOCUS_INDEX_STREAMS = 1;
public static final int VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE = 2;
+public static final int VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS = 3;
public static String enumToString(int v) {
switch(v) {
case VEHICLE_AUDIO_FOCUS_INDEX_FOCUS: return "VEHICLE_AUDIO_FOCUS_INDEX_FOCUS";
case VEHICLE_AUDIO_FOCUS_INDEX_STREAMS: return "VEHICLE_AUDIO_FOCUS_INDEX_STREAMS";
case VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE: return "VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE";
+case VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS: return "VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS";
+default: return "UNKNOWN";
+}
+}
+}
+
+public static class VehicleAudioContextFlag {
+public static final int VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG = 0x1;
+public static final int VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG = 0x2;
+public static final int VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG = 0x4;
+public static final int VEHICLE_AUDIO_CONTEXT_CALL_FLAG = 0x8;
+public static final int VEHICLE_AUDIO_CONTEXT_ALARM_FLAG = 0x10;
+public static final int VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG = 0x20;
+public static final int VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG = 0x40;
+public static final int VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG = 0x80;
+public static final int VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG = 0x100;
+public static final int VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG = 0x200;
+public static final int VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG = 0x400;
+public static final int VEHICLE_AUDIO_CONTEXT_RADIO_FLAG = 0x800;
+public static String enumToString(int v) {
+switch(v) {
+case VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG: return "VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG";
+case VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG: return "VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG";
+case VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG: return "VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG";
+case VEHICLE_AUDIO_CONTEXT_CALL_FLAG: return "VEHICLE_AUDIO_CONTEXT_CALL_FLAG";
+case VEHICLE_AUDIO_CONTEXT_ALARM_FLAG: return "VEHICLE_AUDIO_CONTEXT_ALARM_FLAG";
+case VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG: return "VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG";
+case VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG: return "VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG";
+case VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG: return "VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG";
+case VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG: return "VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG";
+case VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG: return "VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG";
+case VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG: return "VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG";
+case VEHICLE_AUDIO_CONTEXT_RADIO_FLAG: return "VEHICLE_AUDIO_CONTEXT_RADIO_FLAG";
default: return "UNKNOWN";
}
}
@@ -432,36 +467,6 @@
}
}
-public static class VehicleAudioContextFlag {
-public static final int VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG = 0x1;
-public static final int VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG = 0x2;
-public static final int VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG = 0x4;
-public static final int VEHICLE_AUDIO_CONTEXT_CALL_FLAG = 0x8;
-public static final int VEHICLE_AUDIO_CONTEXT_ALARM_FLAG = 0x10;
-public static final int VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG = 0x20;
-public static final int VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG = 0x40;
-public static final int VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG = 0x80;
-public static final int VEHICLE_AUDIO_CONTEXT_CD_ROM = 0x100;
-public static final int VEHICLE_AUDIO_CONTEXT_AUX_AUDIO = 0x200;
-public static final int VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND = 0x400;
-public static String enumToString(int v) {
-switch(v) {
-case VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG: return "VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG";
-case VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG: return "VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG";
-case VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG: return "VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG";
-case VEHICLE_AUDIO_CONTEXT_CALL_FLAG: return "VEHICLE_AUDIO_CONTEXT_CALL_FLAG";
-case VEHICLE_AUDIO_CONTEXT_ALARM_FLAG: return "VEHICLE_AUDIO_CONTEXT_ALARM_FLAG";
-case VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG: return "VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG";
-case VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG: return "VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG";
-case VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG: return "VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG";
-case VEHICLE_AUDIO_CONTEXT_CD_ROM: return "VEHICLE_AUDIO_CONTEXT_CD_ROM";
-case VEHICLE_AUDIO_CONTEXT_AUX_AUDIO: return "VEHICLE_AUDIO_CONTEXT_AUX_AUDIO";
-case VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND: return "VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND";
-default: return "UNKNOWN";
-}
-}
-}
-
public static class VehicleApPowerStateConfigFlag {
public static final int VEHICLE_AP_POWER_STATE_CONFIG_ENABLE_DEEP_SLEEP_FLAG = 0x1;
public static final int VEHICLE_AP_POWER_STATE_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2;
@@ -578,6 +583,20 @@
}
}
+public static class VehicleInstumentClusterType {
+public static final int VEHICLE_INSTRUMENT_CLUSTER_TYPE_NONE = 0;
+public static final int VEHICLE_INSTRUMENT_CLUSTER_TYPE_HAL_INTERFACE = 1;
+public static final int VEHICLE_INSTRUMENT_CLUSTER_TYPE_EXTERNAL_DISPLAY = 2;
+public static String enumToString(int v) {
+switch(v) {
+case VEHICLE_INSTRUMENT_CLUSTER_TYPE_NONE: return "VEHICLE_INSTRUMENT_CLUSTER_TYPE_NONE";
+case VEHICLE_INSTRUMENT_CLUSTER_TYPE_HAL_INTERFACE: return "VEHICLE_INSTRUMENT_CLUSTER_TYPE_HAL_INTERFACE";
+case VEHICLE_INSTRUMENT_CLUSTER_TYPE_EXTERNAL_DISPLAY: return "VEHICLE_INSTRUMENT_CLUSTER_TYPE_EXTERNAL_DISPLAY";
+default: return "UNKNOWN";
+}
+}
+}
+
public static class VehicleValueType {
public static final int VEHICLE_VALUE_TYPE_SHOUD_NOT_USE = 0x00;
public static final int VEHICLE_VALUE_TYPE_STRING = 0x01;
@@ -771,7 +790,7 @@
public static class VehicleGear {
public static final int VEHICLE_GEAR_NEUTRAL = 0x0001;
public static final int VEHICLE_GEAR_REVERSE = 0x0002;
-public static final int VEHICLE_GEAR_PARKING = 0x0004;
+public static final int VEHICLE_GEAR_PARK = 0x0004;
public static final int VEHICLE_GEAR_DRIVE = 0x0008;
public static final int VEHICLE_GEAR_L = 0x0010;
public static final int VEHICLE_GEAR_1 = 0x0010;
@@ -787,7 +806,7 @@
switch(v) {
case VEHICLE_GEAR_NEUTRAL: return "VEHICLE_GEAR_NEUTRAL";
case VEHICLE_GEAR_REVERSE: return "VEHICLE_GEAR_REVERSE";
-case VEHICLE_GEAR_PARKING: return "VEHICLE_GEAR_PARKING";
+case VEHICLE_GEAR_PARK: return "VEHICLE_GEAR_PARK";
case VEHICLE_GEAR_DRIVE: return "VEHICLE_GEAR_DRIVE";
case VEHICLE_GEAR_L: return "VEHICLE_GEAR_L";
case VEHICLE_GEAR_2: return "VEHICLE_GEAR_2";
diff --git a/libvehiclenetwork/libvehiclenetwork-audio-helper/include/vehicle-network-audio-helper-for-c.h b/libvehiclenetwork/libvehiclenetwork-audio-helper/include/vehicle-network-audio-helper-for-c.h
index 3c34f5f..5e97ef6 100644
--- a/libvehiclenetwork/libvehiclenetwork-audio-helper/include/vehicle-network-audio-helper-for-c.h
+++ b/libvehiclenetwork/libvehiclenetwork-audio-helper/include/vehicle-network-audio-helper-for-c.h
@@ -43,7 +43,7 @@
VEHICLE_NETWORK_AUDIO_HELPER_STREAM_3 = 0x8,
};
-#define FOCUS_WAIT_DEFAULT_TIMEOUT_NS 500000000
+#define FOCUS_WAIT_DEFAULT_TIMEOUT_NS 1000000000
/**
* Create helper instance with default timeout. Timer is reset when
diff --git a/libvehiclenetwork/libvehiclenetwork-audio-helper/src/VehicleNetworkAudioHelper.cpp b/libvehiclenetwork/libvehiclenetwork-audio-helper/src/VehicleNetworkAudioHelper.cpp
index 14d7cae..75b6f6b 100644
--- a/libvehiclenetwork/libvehiclenetwork-audio-helper/src/VehicleNetworkAudioHelper.cpp
+++ b/libvehiclenetwork/libvehiclenetwork-audio-helper/src/VehicleNetworkAudioHelper.cpp
@@ -55,7 +55,7 @@
mScratchValueStreamState.value_type = VEHICLE_VALUE_TYPE_INT32_VEC2;
mScratchValueStreamState.timestamp = 0;
mScratchValueFocus.prop = VEHICLE_PROPERTY_AUDIO_FOCUS;
- mScratchValueFocus.value_type = VEHICLE_VALUE_TYPE_INT32_VEC3;
+ mScratchValueFocus.value_type = VEHICLE_VALUE_TYPE_INT32_VEC4;
mScratchValueFocus.timestamp = 0;
updatePropertiesLocked();
return NO_ERROR;
diff --git a/libvehiclenetwork/tool/vehiclehal_code_gen.py b/libvehiclenetwork/tool/vehiclehal_code_gen.py
index d96a87e..e35654f 100755
--- a/libvehiclenetwork/tool/vehiclehal_code_gen.py
+++ b/libvehiclenetwork/tool/vehiclehal_code_gen.py
@@ -37,7 +37,8 @@
* limitations under the License.
*/
-//Autogenerated from vehicle.h. Do not modify manually.
+//Autogenerated from vehicle.h using libvehiclenetwork/tool/vehicle_code_gen.py.
+//Do not modify manually.
package com.android.car.vehiclenetwork;
diff --git a/service/Android.mk b/service/Android.mk
index f0dd68d..8731ccb 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -16,6 +16,9 @@
# Build the Car service.
+#disble build in PDK, missing aidl import breaks build
+ifneq ($(TARGET_BUILD_PDK),true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -34,6 +37,8 @@
LOCAL_JAVA_LIBRARIES += android.car
LOCAL_STATIC_JAVA_LIBRARIES += libvehiclenetwork-java car-systemtest
+LOCAL_JNI_SHARED_LIBRARIES := libjni_car_service
+
include $(BUILD_PACKAGE)
#####################################################################################
@@ -53,3 +58,4 @@
include $(call all-makefiles-under,$(LOCAL_PATH))
+endif #TARGET_BUILD_PDK
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 32647a3..659094a 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -27,7 +27,12 @@
android:icon="@drawable/car_ic_mode"
android:description="@string/car_permission_desc"
android:label="@string/car_permission_label" />
-
+ <permission
+ android:name="android.car.permission.CAR_CAMERA"
+ android:permissionGroup="android.car.permission.CAR_INFORMATION"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_camera"
+ android:description="@string/car_permission_desc_camera" />
<permission
android:name="android.car.permission.CAR_FUEL"
android:permissionGroup="android.car.permission.CAR_INFORMATION"
@@ -85,6 +90,7 @@
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<application android:label="Car service"
android:allowBackup="false"
diff --git a/service/jni/Android.mk b/service/jni/Android.mk
new file mode 100644
index 0000000..ed29126
--- /dev/null
+++ b/service/jni/Android.mk
@@ -0,0 +1,39 @@
+# 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.
+#
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+ find . -name "*.cpp" -and -not -name ".*"))
+
+LOCAL_C_INCLUDES += \
+ system/core/include
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libnativehelper \
+ libutils \
+ libhardware
+
+LOCAL_CFLAGS := \
+ -Wno-unused-parameter \
+
+LOCAL_MODULE := libjni_car_service
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/service/jni/JniInit.cpp b/service/jni/JniInit.cpp
new file mode 100644
index 0000000..751a5e6
--- /dev/null
+++ b/service/jni/JniInit.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CAR.JNI"
+
+#include <utils/Log.h>
+#include <jni.h>
+
+namespace android {
+extern int register_com_android_car_CarCameraService(JNIEnv *env);
+extern int register_com_android_car_CarInputService(JNIEnv *env);
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("GetEnv failed!");
+ return result;
+ }
+ ALOG_ASSERT(env, "Could not retrieve the env!");
+
+ int r = android::register_com_android_car_CarCameraService(env);
+ if (r != 0) {
+ ALOGE("register_com_android_car_CarCameraService failed %d", r);
+ return JNI_ERR;
+ }
+ r = android::register_com_android_car_CarInputService(env);
+ if (r != 0) {
+ ALOGE("register_com_android_car_CarInputService failed %d", r);
+ return JNI_ERR;
+ }
+ return JNI_VERSION_1_6;
+}
diff --git a/service/jni/com_android_car_CarCameraService.cpp b/service/jni/com_android_car_CarCameraService.cpp
new file mode 100644
index 0000000..7306702
--- /dev/null
+++ b/service/jni/com_android_car_CarCameraService.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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 <string.h>
+#include <cutils/log.h>
+#include <hardware/hardware.h>
+#include <hardware/vehicle_camera.h>
+#include <jni.h>
+#include <JNIHelp.h>
+#include <system/window.h>
+
+namespace android {
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeOpen
+ * Signature: ()J
+ */
+static jlong com_android_car_CarCameraService_nativeOpen
+ (JNIEnv *env, jobject obj)
+{
+ vehicle_camera_module_t *module = NULL;
+
+ hw_get_module(VEHICLE_CAMERA_HARDWARE_MODULE_ID, (const hw_module_t**)&module);
+
+ if (module == NULL) {
+ ALOGE("JNI Camera: nativeOpen failed!");
+ }
+ return (jlong)module;
+}
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeClose
+ * Signature: (J)V
+ */
+static void com_android_car_CarCameraService_nativeClose
+ (JNIEnv *env, jobject, jlong dev)
+{
+ vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+
+ if (device != NULL) {
+ device->common.close((struct hw_device_t*)device);
+ }
+}
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeGetSupportedCameras
+ * Signature: (J)[I
+ */
+static jintArray com_android_car_CarCameraService_nativeGetSupportedCameras
+ (JNIEnv *env, jobject, jlong mod)
+{
+ int i;
+ vehicle_camera_module_t *module = (vehicle_camera_module_t*)mod;
+ const uint32_t *camera_list;
+ uint32_t numCameras;
+ jintArray outArray = NULL;
+
+ camera_list = module->get_camera_device_list(&numCameras);
+
+ if ((numCameras > 0) && (camera_list != NULL)) {
+ outArray = env->NewIntArray(numCameras);
+ }
+
+ if (outArray == NULL) {
+ return NULL;
+ }
+ // Copy camera list to output array
+ env->SetIntArrayRegion(outArray, 0, numCameras, (jint*)camera_list);
+ return outArray;
+}
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeGetDevice
+ * Signature: (JI)J
+ */
+static jlong com_android_car_CarCameraService_nativeGetDevice
+ (JNIEnv *env, jobject, jlong mod, jint cameraType)
+{
+ const char *cameraTypeString[] = {
+ VEHICLE_CAMERA_RVC_DEVICE,
+ };
+ vehicle_camera_module_t *module = (vehicle_camera_module_t*)mod;
+ hw_device_t *device = NULL;
+
+ if (module != NULL) {
+ module->common.methods->open((hw_module_t*)module, cameraTypeString[cameraType], &device);
+ }
+
+ if (device == NULL) {
+ ALOGE("JNI Camera: nativeGetDevice failed!");
+ }
+ return (jlong)device;
+}
+
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeGetCapabilities
+ * Signature: (J)I
+ */
+static jint com_android_car_CarCameraService_nativeGetCapabilities
+ (JNIEnv *env, jobject, jlong dev)
+{
+ vehicle_camera_cap_t cap;
+ jint capabilities = 0;
+ vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+ if (device != NULL) {
+ // Fetch the capabilities
+ int errCode = device->get_capabilities(device, &cap);
+
+ if (errCode == 0) {
+ capabilities = cap.capabilites_flags;
+ } else {
+ ALOGE("JNI Camera: nativeGetCapabilites errCode = %d", errCode);
+ }
+ } else {
+ ALOGE("JNI Camera: nativeGetCapabilities!");
+ }
+
+ return capabilities;
+}
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeGetCameraCrop
+ * Signature: (J)Landroid/graphics/Rect;
+ */
+static jobject com_android_car_CarCameraService_nativeGetCameraCrop
+ (JNIEnv *env, jobject, jlong dev)
+{
+ vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+ jobject retObj = NULL;
+
+ if (device != NULL) {
+ // Get the crop values
+ android_native_rect_t rect;
+ device->get_camera_crop(device, &rect);
+
+ // Get the class
+ jclass cls = env->FindClass("android/graphics/Rect");
+ jmethodID midInit = env->GetMethodID(cls, "<init>", "(IIII)V");
+
+ // Instantiate a new java CarRect objected with the current values
+ retObj = env->NewObject(cls, midInit, rect.left, rect.top, rect.right, rect.bottom);
+ }
+
+ return retObj;
+}
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeSetCameraCrop
+ * Signature: (JLandroid/graphics/Rect;)V
+ */
+static void com_android_car_CarCameraService_nativeSetCameraCrop
+ (JNIEnv *env, jobject, jlong dev, jobject jrect)
+{
+ vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+ if (device != NULL) {
+ android_native_rect_t rect;
+
+ jclass cls = env->GetObjectClass(jrect);
+ jfieldID fidLeft = env->GetFieldID(cls, "left", "I");
+ jfieldID fidTop = env->GetFieldID(cls, "top", "I");
+ jfieldID fidRight = env->GetFieldID(cls, "right", "I");
+ jfieldID fidBottom = env->GetFieldID(cls, "bottom", "I");
+
+ rect.left = (uint32_t)env->GetIntField(jrect, fidLeft);
+ rect.top = (uint32_t)env->GetIntField(jrect, fidTop);
+ rect.right = (uint32_t)env->GetIntField(jrect, fidRight);
+ rect.bottom = (uint32_t)env->GetIntField(jrect, fidBottom);
+
+ device->set_camera_crop(device, &rect);
+ }
+}
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeGetCameraPosition
+ * Signature: (J)Landroid/graphics/Rect;
+ */
+static jobject com_android_car_CarCameraService_nativeGetCameraPosition
+ (JNIEnv *env, jobject, jlong dev)
+{
+ vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+ jobject retObj = NULL;
+
+ if (device != NULL) {
+ // Get the position values
+ android_native_rect_t rect;
+ device->get_camera_position(device, &rect);
+
+ // Get the class
+ jclass cls = env->FindClass("android/graphics/Rect");
+ jmethodID midInit = env->GetMethodID(cls, "<init>", "(IIII)V");
+
+ // Instantiate a new java CarRect objected with the current values
+ retObj = env->NewObject(cls, midInit, rect.left, rect.top, rect.right, rect.bottom);
+ }
+
+ return retObj;
+}
+
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeSetCameraPosition
+ * Signature: (JLandroid/graphics/Rect;)V
+ */
+static void com_android_car_CarCameraService_nativeSetCameraPosition
+ (JNIEnv *env, jobject, jlong dev, jobject jrect)
+{
+ vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+ if (device != NULL) {
+ android_native_rect_t rect;
+
+ jclass cls = env->GetObjectClass(jrect);
+ jfieldID fidLeft = env->GetFieldID(cls, "left", "I");
+ jfieldID fidTop = env->GetFieldID(cls, "top", "I");
+ jfieldID fidRight = env->GetFieldID(cls, "right", "I");
+ jfieldID fidBottom = env->GetFieldID(cls, "bottom", "I");
+
+ rect.left = (uint32_t)env->GetIntField(jrect, fidLeft);
+ rect.top = (uint32_t)env->GetIntField(jrect, fidTop);
+ rect.right = (uint32_t)env->GetIntField(jrect, fidRight);
+ rect.bottom = (uint32_t)env->GetIntField(jrect, fidBottom);
+
+ device->set_camera_position(device, &rect);
+ }
+}
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeGetCameraState
+ * Signature: (J)Landroid/car/hardware/camera/CarCameraState;
+ */
+static jobject com_android_car_CarCameraService_nativeGetCameraState
+ (JNIEnv *env, jobject, jlong dev)
+{
+ vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+ jobject retObj = NULL;
+
+ if (device != NULL) {
+ vehicle_camera_state_t state;
+ device->get_camera_state(device, &state);
+
+ // Get the class
+ jclass cls = env->FindClass("android/car/hardware/camera/CarCameraState");
+ jmethodID midInit = env->GetMethodID(cls, "<init>", "(ZZ)V");
+
+ // Instantiate a new java CarRect objected with the current values
+ retObj = env->NewObject(cls, midInit, state.overlay_on, state.camera_on);
+ }
+
+ return retObj;
+}
+
+
+/*
+ * Class: com_android_car_CarCameraService
+ * Method: nativeSetCameraState
+ * Signature: (JLandroid/car/hardware/camera/CarCameraState;)V
+ */
+static void com_android_car_CarCameraService_nativeSetCameraState
+ (JNIEnv *env, jobject, jlong dev, jobject jstate)
+{
+ vehicle_camera_device_t *device = (vehicle_camera_device_t*)dev;
+ if (device != NULL) {
+ vehicle_camera_state_t state;
+
+ jclass cls = env->GetObjectClass(jstate);
+ jmethodID midCamera = env->GetMethodID(cls, "getCameraIsOn", "()Z");
+ jmethodID midOverlay = env->GetMethodID(cls, "getOverlayIsOn", "()Z");
+
+ state.overlay_on = (uint32_t)env->CallBooleanMethod(jstate, midOverlay);
+ state.camera_on = (uint32_t)env->CallBooleanMethod(jstate, midCamera);
+
+ device->set_camera_state(device, &state);
+ }
+}
+
+static JNINativeMethod gMethods[] = {
+ { "nativeOpen", "()J", (void*)com_android_car_CarCameraService_nativeOpen },
+ { "nativeClose", "(J)V", (void*)com_android_car_CarCameraService_nativeClose },
+ { "nativeGetSupportedCameras", "(J)[I", (void*)com_android_car_CarCameraService_nativeGetSupportedCameras },
+ { "nativeGetDevice", "(JI)J", (void*)com_android_car_CarCameraService_nativeGetDevice },
+ { "nativeGetCapabilities", "(J)I", (void*)com_android_car_CarCameraService_nativeGetCapabilities },
+ { "nativeGetCameraCrop", "(J)Landroid/graphics/Rect;", (void*)com_android_car_CarCameraService_nativeGetCameraCrop },
+ { "nativeSetCameraCrop", "(JLandroid/graphics/Rect;)V", (void*)com_android_car_CarCameraService_nativeSetCameraCrop },
+ { "nativeGetCameraPosition", "(J)Landroid/graphics/Rect;", (void*)com_android_car_CarCameraService_nativeGetCameraPosition },
+ { "nativeSetCameraPosition", "(JLandroid/graphics/Rect;)V", (void*)com_android_car_CarCameraService_nativeSetCameraPosition },
+ { "nativeGetCameraState", "(J)Landroid/car/hardware/camera/CarCameraState;", (void*)com_android_car_CarCameraService_nativeGetCameraState },
+ { "nativeSetCameraState", "(JLandroid/car/hardware/camera/CarCameraState;)V", (void*)com_android_car_CarCameraService_nativeSetCameraState },
+};
+
+int register_com_android_car_CarCameraService(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, "com/android/car/CarCameraService",
+ gMethods, NELEM(gMethods));
+}
+
+} // namespace android
+
+
diff --git a/service/jni/com_android_car_CarInputService.cpp b/service/jni/com_android_car_CarInputService.cpp
new file mode 100644
index 0000000..1b02c12
--- /dev/null
+++ b/service/jni/com_android_car_CarInputService.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "CAR.INPUT"
+
+#include <string.h>
+#include <sys/time.h>
+#include <linux/input.h>
+#include <jni.h>
+#include <JNIHelp.h>
+#include <android/keycodes.h>
+#include <cutils/log.h>
+#include <utils/Errors.h>
+
+
+namespace android {
+
+static int androidKeyCodeToLinuxKeyCode(int androidKeyCode) {
+ switch (androidKeyCode) {
+ case AKEYCODE_VOLUME_UP:
+ return KEY_VOLUMEUP;
+ case AKEYCODE_VOLUME_DOWN:
+ return KEY_VOLUMEDOWN;
+ case AKEYCODE_CALL:
+ return KEY_SEND;
+ case AKEYCODE_ENDCALL:
+ return KEY_END;
+ /* TODO add more keys like these:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:*/
+ case AKEYCODE_VOICE_ASSIST:
+ return KEY_MICMUTE;
+ default:
+ ALOGW("Unmapped android key code %d dropped", androidKeyCode);
+ return 0;
+ }
+}
+
+/*
+ * Class: com_android_car_CarInputService
+ * Method: nativeInjectKeyEvent
+ * Signature: (IIZ)I
+ */
+static jint com_android_car_CarInputService_nativeInjectKeyEvent
+ (JNIEnv *env, jobject /*object*/, jint fd, jint keyCode, jboolean down) {
+ int linuxKeyCode = androidKeyCodeToLinuxKeyCode(keyCode);
+ if (linuxKeyCode == 0) {
+ return BAD_VALUE;
+ }
+ struct input_event ev[2];
+ memset(reinterpret_cast<void*>(&ev), 0, sizeof(ev));
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ // kernel driver is not using time now, but set it to be safe.
+ ev[0].time = now;
+ ev[0].type = EV_KEY;
+ ev[0].code = linuxKeyCode;
+ ev[0].value = (down ? 1 : 0);
+ // force delivery and flushing
+ ev[1].time = now;
+ ev[1].type = EV_SYN;
+ ev[1].code = SYN_REPORT;
+ ev[1].value = 0;
+ ALOGI("injectKeyEvent down %d keyCode %d, value %d", down, ev[0].code, ev[0].value);
+ int r = write(fd, reinterpret_cast<void*>(&ev), sizeof(ev));
+ if (r != sizeof(ev)) {
+ return -EIO;
+ }
+ return 0;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "nativeInjectKeyEvent", "(IIZ)I",
+ (void*)com_android_car_CarInputService_nativeInjectKeyEvent },
+};
+
+int register_com_android_car_CarInputService(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, "com/android/car/CarInputService",
+ gMethods, NELEM(gMethods));
+}
+
+} // namespace android
diff --git a/service/res/layout/instrument_cluster.xml b/service/res/layout/instrument_cluster.xml
new file mode 100644
index 0000000..1b5387b
--- /dev/null
+++ b/service/res/layout/instrument_cluster.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" android:id="@+id/instrument_cluster_layout"
+ android:theme="@android:style/Theme.Material"
+ android:background="@android:color/background_dark">
+</FrameLayout>
\ No newline at end of file
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 165688d..24ef730 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -43,6 +43,10 @@
<item>"0:call,media,unknown#1:nav_guidance,voice_command,alarm,notification,system,safety"</item>
</string-array>
+ <!-- This is kernel device node to allow input event injection for key inputs coming
+ from vehicle hal -->
+ <string name="inputInjectionDeviceNode">/dev/input/event2</string>
+
<string name="instrumentClusterRendererPackage">android.car.cluster.demorenderer</string>
<string name="instrumentClusterRendererFactoryClass">android.car.cluster.demorenderer.InstrumentClusterRendererFactory</string>
</resources>
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index 4606f21..d2ee58a 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -19,14 +19,19 @@
<string name="car_permission_label">Car information</string>
<!-- Permission text: can access your car's information [CHAR LIMIT=NONE] -->
<string name="car_permission_desc">Access your car\'s information.</string>
+ <!-- Permission text: apps can control car camera [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_camera">Car Camera</string>
+ <!-- Permission text: apps can control car amera [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_camera">Access your car\'s camera(s).</string>
<!-- Permission text: can access your car's fuel level [CHAR LIMIT=NONE] -->
<string name="car_permission_label_fuel">Car fuel level</string>
<!-- Permission text: can access your car's fuel level [CHAR LIMIT=NONE] -->
<string name="car_permission_desc_fuel">Access your car\'s fuel level information.</string>
- <!-- Permission text: can access your car's mileage information [CHAR LIMIT=NONE] -->
+ <!-- Permission text: apps can control car hvac [CHAR LIMIT=NONE] -->
<string name="car_permission_label_hvac">Car Hvac</string>
<!-- Permission text: apps can control car hvac [CHAR LIMIT=NONE] -->
<string name="car_permission_desc_hvac">Access your car\'s hvac.</string>
+ <!-- Permission text: can access your car's mileage information [CHAR LIMIT=NONE] -->
<string name="car_permission_label_mileage">Car mileage</string>
<!-- Permission text: can access your car's mileage information [CHAR LIMIT=NONE] -->
<string name="car_permission_desc_mileage">Access your car\'s mileage information.</string>
diff --git a/service/src/com/android/car/AppContextService.java b/service/src/com/android/car/AppContextService.java
index 71bd539..edc10bd 100644
--- a/service/src/com/android/car/AppContextService.java
+++ b/service/src/com/android/car/AppContextService.java
@@ -41,7 +41,6 @@
/** K: context flag, V: client owning it */
private final HashMap<Integer, ClientInfo> mContextOwners = new HashMap<>();
private int mActiveContexts;
- private boolean mCallActive;
private final HandlerThread mHandlerThread;
private final DispatchHandler mDispatchHandler;
@@ -112,6 +111,8 @@
if ((c & contexts) != 0 && (c & alreadyOwnedContexts) == 0) {
ClientInfo ownerInfo = mContextOwners.get(c);
if (ownerInfo != null && ownerInfo != info) {
+ //TODO check if current owner is having fore-ground activity. If yes,
+ //reject request. Always grant if requestor is fore-ground activity.
ownerInfo.setOwnedContexts(ownerInfo.getOwnedContexts() & ~c);
mDispatchHandler.requestAppContextOwnershipLossDispatch(
ownerInfo.binderInterface, c);
@@ -132,7 +133,6 @@
Log.i(CarLog.TAG_APP_CONTEXT, "setting context " +
Integer.toHexString(addedContexts) + "," + info.toString());
}
- mDispatchHandler.requestHalNotification(mActiveContexts, mCallActive);
for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
mAllClients.getInterfaces()) {
ClientInfo clientInfo = (ClientInfo) client;
@@ -176,7 +176,6 @@
Log.i(CarLog.TAG_APP_CONTEXT, "resetting context " +
Integer.toHexString(removedContexts) + "," + info.toString());
}
- mDispatchHandler.requestHalNotification(mActiveContexts, mCallActive);
for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
mAllClients.getInterfaces()) {
ClientInfo clientInfo = (ClientInfo) client;
@@ -201,17 +200,6 @@
mAllClients.clear();
mContextOwners.clear();
mActiveContexts = 0;
- mCallActive = false;
- }
- }
-
- public void handleCallStateChange(boolean callActive) {
- synchronized (this) {
- if (callActive == mCallActive) { // no change
- return;
- }
- mCallActive = callActive;
- mDispatchHandler.requestHalNotification(mActiveContexts, mCallActive);
}
}
@@ -229,8 +217,7 @@
public void dump(PrintWriter writer) {
writer.println("**AppContextService**");
synchronized (this) {
- writer.println("mActiveContexts:" + Integer.toHexString(mActiveContexts) +
- ",mCallState:" + mCallActive);
+ writer.println("mActiveContexts:" + Integer.toHexString(mActiveContexts));
for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
mAllClients.getInterfaces()) {
ClientInfo clientInfo = (ClientInfo) client;
@@ -266,13 +253,6 @@
}
}
- private void notifyAppContextChangeToHal(int contexts, boolean callState) {
- VehicleHal.getInstance().updateAppContext(
- (contexts & CarAppContextManager.APP_CONTEXT_NAVIGATION) != 0/*navigation*/,
- (contexts & CarAppContextManager.APP_CONTEXT_VOICE_COMMAND) != 0/*voice*/,
- callState /*callActive*/);
- }
-
private static class ClientHolder extends BinderInterfaceContainer<IAppContextListener> {
private ClientHolder(AppContextService service) {
super(service);
@@ -331,7 +311,6 @@
private class DispatchHandler extends Handler {
private static final int MSG_DISPATCH_OWNERSHIP_LOSS = 0;
private static final int MSG_DISPATCH_CONTEXT_CHANGE = 1;
- private static final int MSG_NOTIFY_HAL = 2;
private DispatchHandler(Looper looper) {
super(looper);
@@ -348,11 +327,6 @@
sendMessage(msg);
}
- private void requestHalNotification(int contexts, boolean callState) {
- Message msg = obtainMessage(MSG_NOTIFY_HAL, contexts, callState ? 1 : 0);
- sendMessage(msg);
- }
-
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -362,9 +336,6 @@
case MSG_DISPATCH_CONTEXT_CHANGE:
dispatchAppContextChange((IAppContextListener) msg.obj, msg.arg1);
break;
- case MSG_NOTIFY_HAL:
- notifyAppContextChangeToHal(msg.arg1, msg.arg2 == 1);
- break;
}
}
}
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index 5bbad78..5019fb4 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -65,6 +65,9 @@
private LinkedList<AudioFocusInfo> mPendingFocusChanges = new LinkedList<>();
@GuardedBy("mLock")
private AudioFocusInfo mTopFocusInfo = null;
+ /** previous top which may be in ducking state */
+ @GuardedBy("mLock")
+ private AudioFocusInfo mSecondFocusInfo = null;
private AudioRoutingPolicy mAudioRoutingPolicy;
private final AudioManager mAudioManager;
@@ -78,8 +81,8 @@
private boolean mRadioActive = false;
@GuardedBy("mLock")
private boolean mCallActive = false;
-
- private final AppContextService mAppContextService;
+ @GuardedBy("mLock")
+ private int mCurrentAudioContexts = 0;
private final AudioAttributes mAttributeBottom =
CarAudioAttributesUtil.getAudioAttributesForCarUsage(
@@ -88,7 +91,7 @@
CarAudioAttributesUtil.getAudioAttributesForCarUsage(
CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY);
- public CarAudioService(Context context, AppContextService appContextService) {
+ public CarAudioService(Context context) {
mAudioHal = VehicleHal.getInstance().getAudioHal();
mContext = context;
mFocusHandlerThread = new HandlerThread(CarLog.TAG_AUDIO);
@@ -97,7 +100,6 @@
mFocusHandler = new CarAudioFocusChangeHandler(mFocusHandlerThread.getLooper());
mVolumeHandler = new CarAudioVolumeHandler(Looper.getMainLooper());
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- mAppContextService = appContextService;
}
@Override
@@ -125,6 +127,7 @@
mBottomFocusState = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
}
mCurrentFocusState = currentState;
+ mCurrentAudioContexts = 0;
}
}
int r = mAudioManager.registerAudioPolicy(mAudioPolicy);
@@ -159,6 +162,7 @@
writer.println("*CarAudioService*");
writer.println(" mCurrentFocusState:" + mCurrentFocusState +
" mLastFocusRequestToCar:" + mLastFocusRequestToCar);
+ writer.println(" mCurrentAudioContexts:0x" + Integer.toHexString(mCurrentAudioContexts));
writer.println(" mCallActive:" + mCallActive + " mRadioActive:" + mRadioActive);
mAudioRoutingPolicy.dump(writer);
}
@@ -426,16 +430,17 @@
int logicalStreamTypeForTop = CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib);
int physicalStreamTypeForTop = mAudioRoutingPolicy.getPhysicalStreamForLogicalStream(
logicalStreamTypeForTop);
+ int audioContexts = 0;
if (logicalStreamTypeForTop == CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL) {
if (!mCallActive) {
mCallActive = true;
- mAppContextService.handleCallStateChange(mCallActive);
+ audioContexts |= AudioHalService.AUDIO_CONTEXT_CALL_FLAG;
}
} else {
if (mCallActive) {
mCallActive = false;
- mAppContextService.handleCallStateChange(mCallActive);
}
+ audioContexts = AudioHalService.logicalStreamToHalContextType(logicalStreamTypeForTop);
}
// other apps having focus
int focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE;
@@ -445,6 +450,8 @@
case AudioManager.AUDIOFOCUS_GAIN:
if (isFocusFromRadio(mTopFocusInfo)) {
mRadioActive = true;
+ // audio context sending is only for audio from android.
+ audioContexts = 0;
} else {
mRadioActive = false;
}
@@ -457,6 +464,7 @@
focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT;
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
+ audioContexts |= getAudioContext(mSecondFocusInfo);
focusToRequest =
AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK;
switch (mCurrentFocusState.focusState) {
@@ -466,7 +474,7 @@
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT:
streamsToRequest |= mCurrentFocusState.streams;
- //TODO is there a need to change this to GAIN_TRANSIENT?
+ focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT;
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS:
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT:
@@ -482,29 +490,57 @@
break;
}
if (mRadioActive) {
- extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG;
// TODO any need to keep media stream while radio is active?
// Most cars do not allow that, but if mixing is possible, it can take media stream.
// For now, assume no mixing capability.
int radioPhysicalStream = mAudioRoutingPolicy.getPhysicalStreamForLogicalStream(
CarAudioManager.CAR_AUDIO_USAGE_MUSIC);
- streamsToRequest &= ~(0x1 << radioPhysicalStream);
+ if (!isFocusFromRadio(mTopFocusInfo) &&
+ (physicalStreamTypeForTop == radioPhysicalStream)) {
+ Log.i(CarLog.TAG_AUDIO, "Top stream is taking the same stream:" +
+ physicalStreamTypeForTop + " as radio, stopping radio");
+ // stream conflict here. radio cannot be played
+ extFocus = 0;
+ mRadioActive = false;
+ audioContexts &= ~AudioHalService.AUDIO_CONTEXT_RADIO_FLAG;
+ } else {
+ extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG;
+ streamsToRequest &= ~(0x1 << radioPhysicalStream);
+ }
} else if (streamsToRequest == 0) {
+ mCurrentAudioContexts = 0;
mFocusHandler.handleFocusReleaseRequest();
return false;
}
- return sendFocusRequestToCarIfNecessaryLocked(focusToRequest, streamsToRequest, extFocus);
+ return sendFocusRequestToCarIfNecessaryLocked(focusToRequest, streamsToRequest, extFocus,
+ audioContexts);
+ }
+
+ private static int getAudioContext(AudioFocusInfo info) {
+ if (info == null) {
+ return 0;
+ }
+ AudioAttributes attrib = info.getAttributes();
+ if (attrib == null) {
+ return AudioHalService.AUDIO_CONTEXT_UNKNOWN_FLAG;
+ }
+ return AudioHalService.logicalStreamToHalContextType(
+ CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib));
}
private boolean sendFocusRequestToCarIfNecessaryLocked(int focusToRequest,
- int streamsToRequest, int extFocus) {
- if (needsToSendFocusRequestLocked(focusToRequest, streamsToRequest, extFocus)) {
+ int streamsToRequest, int extFocus, int audioContexts) {
+ if (needsToSendFocusRequestLocked(focusToRequest, streamsToRequest, extFocus,
+ audioContexts)) {
mLastFocusRequestToCar = FocusRequest.create(focusToRequest, streamsToRequest,
extFocus);
+ mCurrentAudioContexts = audioContexts;
if (DBG) {
- Log.d(TAG_FOCUS, "focus request to car:" + mLastFocusRequestToCar);
+ Log.d(TAG_FOCUS, "focus request to car:" + mLastFocusRequestToCar + " context:0x" +
+ Integer.toHexString(audioContexts));
}
- mAudioHal.requestAudioFocusChange(focusToRequest, streamsToRequest, extFocus);
+ mAudioHal.requestAudioFocusChange(focusToRequest, streamsToRequest, extFocus,
+ audioContexts);
try {
mLock.wait(FOCUS_RESPONSE_WAIT_TIMEOUT_MS);
} catch (InterruptedException e) {
@@ -516,10 +552,13 @@
}
private boolean needsToSendFocusRequestLocked(int focusToRequest, int streamsToRequest,
- int extFocus) {
+ int extFocus, int audioContexts) {
if (streamsToRequest != mCurrentFocusState.streams) {
return true;
}
+ if (audioContexts != mCurrentAudioContexts) {
+ return true;
+ }
if ((extFocus & mCurrentFocusState.externalFocus) != extFocus) {
return true;
}
@@ -578,6 +617,11 @@
if (DBG) {
Log.d(TAG_FOCUS, "top focus changed to:" + dumpAudioFocusInfo(newTopInfo));
}
+ if (newTopInfo.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) {
+ mSecondFocusInfo = mTopFocusInfo;
+ } else {
+ mSecondFocusInfo = null;
+ }
mTopFocusInfo = newTopInfo;
focusRequested = reevaluateCarAudioFocusLocked();
if (DBG) {
@@ -591,6 +635,7 @@
mLastFocusRequestToCar);
// no response. so reset to loss.
mFocusReceived = FocusState.STATE_LOSS;
+ mCurrentAudioContexts = 0;
}
}
// handle it if there was response or force handle it for timeout.
@@ -610,7 +655,7 @@
mLastFocusRequestToCar = FocusRequest.STATE_RELEASE;
sent = true;
mAudioHal.requestAudioFocusChange(
- AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, 0);
+ AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, 0, 0);
try {
mLock.wait(FOCUS_RESPONSE_WAIT_TIMEOUT_MS);
} catch (InterruptedException e) {
diff --git a/service/src/com/android/car/CarCameraService.java b/service/src/com/android/car/CarCameraService.java
new file mode 100644
index 0000000..7d2608b
--- /dev/null
+++ b/service/src/com/android/car/CarCameraService.java
@@ -0,0 +1,224 @@
+/*
+ * 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.car;
+
+import android.content.Context;
+import android.car.hardware.camera.CarCameraState;
+import android.car.hardware.camera.ICarCamera;
+import android.graphics.Rect;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Set;
+
+public class CarCameraService extends ICarCamera.Stub implements CarServiceBase {
+ public static final boolean DBG = false;
+ public static final String TAG = CarLog.TAG_CAMERA + ".CarCameraService";
+
+ private final Context mContext;
+ private long mModule;
+ private final HashMap<Integer, Long> mDeviceMap;
+
+ public CarCameraService(Context context) {
+ mContext = context;
+ mDeviceMap = new HashMap<Integer, Long>();
+ }
+
+ @Override
+ public synchronized void init() {
+ if (DBG) {
+ Log.d(TAG, "init called");
+ }
+ mModule = nativeOpen();
+
+ if (mModule != 0) {
+ int[] cameraType = nativeGetSupportedCameras(mModule);
+
+ if (cameraType != null) {
+ for (int i : cameraType) {
+ long devicePtr = nativeGetDevice(mModule, i);
+ if (devicePtr == 0) {
+ Log.e(TAG, "Null device pointer returned for cameraType = " + i);
+ } else {
+ mDeviceMap.put(i, devicePtr);
+ }
+ }
+ } else {
+ Log.e(TAG, "No car cameras are supported");
+ }
+ } else {
+ Log.w(TAG, "Cannot load camera module");
+ }
+ }
+
+ @Override
+ public synchronized void release() {
+ if (DBG) {
+ Log.d(TAG, "release called");
+ }
+ Collection<Long> devices = mDeviceMap.values();
+ for (Long device : devices) {
+ nativeClose(device);
+ }
+ mDeviceMap.clear();
+ mModule = 0;
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ // TODO
+ }
+
+ @Override
+ public int[] getCameraList() {
+ if (DBG) {
+ Log.d(TAG, "getCameraList called");
+ }
+ Set<Integer> keySet;
+ synchronized (this) {
+ keySet = mDeviceMap.keySet();
+ }
+
+ int keySetSize = keySet.size();
+
+ if (keySetSize > 0) {
+ int[] keyArray = new int[keySet.size()];
+ int i = 0;
+ for (Integer key : keySet) {
+ keyArray[i++] = key.intValue();
+ }
+ return keyArray;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public int getCapabilities(int cameraType) {
+ if (DBG) {
+ Log.d(TAG, "getCapabilities called, type = " + String.valueOf(cameraType));
+ }
+ synchronized (this) {
+ long device = getDeviceIdLocked(cameraType);
+ return nativeGetCapabilities(device);
+ }
+ }
+
+ @Override
+ public Rect getCameraCrop(int cameraType) {
+ Rect rect;
+ synchronized (this) {
+ long device = getDeviceIdLocked(cameraType);
+ rect = nativeGetCameraCrop(device);
+ }
+ if(DBG && (rect != null)) {
+ Log.d(TAG, "getCameraCrop called: " + rect.toString());
+ }
+ return rect;
+ }
+
+ @Override
+ public void setCameraCrop(int cameraType, Rect rect) {
+ if (DBG) {
+ Log.d(TAG, "setCameraCrop called." + rect.toString());
+ }
+ synchronized (this) {
+ long device = getDeviceIdLocked(cameraType);
+ nativeSetCameraCrop(device, rect);
+ }
+ }
+
+ @Override
+ public Rect getCameraPosition(int cameraType) {
+ Rect rect;
+ synchronized (this) {
+ long device = getDeviceIdLocked(cameraType);
+ rect = nativeGetCameraPosition(device);
+ }
+ if(DBG && (rect != null)) {
+ Log.d(TAG, "getCameraPosition called: " + rect.toString());
+ }
+ return rect;
+ }
+
+ @Override
+ public void setCameraPosition(int cameraType, Rect rect) {
+ if (DBG) {
+ Log.d(TAG, "setCameraPosition called." + rect.toString());
+ }
+ synchronized (this) {
+ long device = getDeviceIdLocked(cameraType);
+ nativeSetCameraPosition(device, rect);
+ }
+ }
+
+ @Override
+ public CarCameraState getCameraState(int cameraType) {
+ CarCameraState state;
+ synchronized (this) {
+ long device = getDeviceIdLocked(cameraType);
+ state = nativeGetCameraState(device);
+ }
+ if(DBG && (state != null)) {
+ Log.d(TAG, "getCameraState called: " + state.toString());
+ }
+ return state;
+ }
+
+ @Override
+ public void setCameraState(int cameraType, CarCameraState state) {
+ if (DBG) {
+ Log.d(TAG, "setCameraState called. state: " + state.toString());
+ }
+ synchronized (this) {
+ long device = getDeviceIdLocked(cameraType);
+ nativeSetCameraState(device, state);
+ }
+ }
+
+ /**
+ * Validates that the cameraType is available and ready to be used.
+ * @param cameraType
+ * @return
+ */
+ private long getDeviceIdLocked(int cameraType) {
+ Long deviceId = mDeviceMap.get(cameraType);
+
+ if (deviceId == null) {
+ throw new IllegalArgumentException("cameraType " + cameraType + " doesn't exist in"
+ + "device map");
+ }
+ return deviceId;
+ }
+
+ /*
+ * Native function definitions
+ */
+ private native long nativeOpen();
+ private native void nativeClose(long module);
+ private native int[] nativeGetSupportedCameras(long module);
+ private native long nativeGetDevice(long module, int cameraType);
+ private native int nativeGetCapabilities(long device);
+ private native Rect nativeGetCameraCrop(long device);
+ private native void nativeSetCameraCrop(long device, Rect rect);
+ private native Rect nativeGetCameraPosition(long device);
+ private native void nativeSetCameraPosition(long device, Rect rect);
+ private native CarCameraState nativeGetCameraState(long device);
+ private native void nativeSetCameraState(long device, CarCameraState state);
+}
diff --git a/service/src/com/android/car/CarInputService.java b/service/src/com/android/car/CarInputService.java
new file mode 100644
index 0000000..8b0bd3d
--- /dev/null
+++ b/service/src/com/android/car/CarInputService.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.speech.RecognizerIntent;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import com.android.car.hal.InputHalService;
+import com.android.car.hal.VehicleHal;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class CarInputService implements CarServiceBase, InputHalService.InputListener {
+
+ public interface KeyEventListener {
+ void onKeyEvent(KeyEvent event);
+ }
+
+ private static final long VOICE_LONG_PRESS_TIME_MS = 1000;
+
+ private final Context mContext;
+
+ private KeyEventListener mVoiceAssitantKeyListener;
+ private KeyEventListener mLongVoiceAssitantKeyListener;
+ private long mLastVoiceKeyDownTime = 0;
+
+ private KeyEventListener mInstumentClusterKeyListener;
+
+ private ParcelFileDescriptor mInjectionDeviceFd;
+
+ private int mKeyEventCount = 0;
+
+ public CarInputService(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Set listener for listening voice assistant key event. Setting to null stops listening.
+ * If listener is not set, default behavior will be done for short press.
+ * If listener is set, short key press will lead into calling the listener.
+ * @param listener
+ */
+ public void setVoiceAssitantKeyListener(KeyEventListener listener) {
+ synchronized (this) {
+ mVoiceAssitantKeyListener = listener;
+ }
+ }
+
+ /**
+ * Set listener for listening long voice assistant key event. Setting to null stops listening.
+ * If listener is not set, default behavior will be done for long press.
+ * If listener is set, short long press will lead into calling the listener.
+ * @param listener
+ */
+ public void setLongVoiceAssitantKeyListener(KeyEventListener listener) {
+ synchronized (this) {
+ mLongVoiceAssitantKeyListener = listener;
+ }
+ }
+
+ public void setInstrumentClusterKeyListener(KeyEventListener listener) {
+ synchronized (this) {
+ mInstumentClusterKeyListener = listener;
+ }
+ }
+
+ @Override
+ public void init() {
+ InputHalService hal = VehicleHal.getInstance().getInputHal();
+ if (!hal.isKeyInputSupported()) {
+ Log.w(CarLog.TAG_INPUT, "Hal does not support key input.");
+ return;
+ }
+ String injectionDevice = mContext.getResources().getString(
+ R.string.inputInjectionDeviceNode);
+ ParcelFileDescriptor file = null;
+ try {
+ file = ParcelFileDescriptor.open(new File(injectionDevice),
+ ParcelFileDescriptor.MODE_READ_WRITE);
+ } catch (FileNotFoundException e) {
+ Log.w(CarLog.TAG_INPUT, "cannot open device for input injection:" + injectionDevice);
+ return;
+ }
+ synchronized (this) {
+ mInjectionDeviceFd = file;
+ }
+ hal.setInputListener(this);
+ }
+
+ @Override
+ public void release() {
+ synchronized (this) {
+ mVoiceAssitantKeyListener = null;
+ mLongVoiceAssitantKeyListener = null;
+ mInstumentClusterKeyListener = null;
+ if (mInjectionDeviceFd != null) {
+ try {
+ mInjectionDeviceFd.close();
+ } catch (IOException e) {
+ }
+ }
+ mInjectionDeviceFd = null;
+ mKeyEventCount = 0;
+ }
+ }
+
+ @Override
+ public void onKeyEvent(KeyEvent event, int targetDisplay) {
+ synchronized (this) {
+ mKeyEventCount++;
+ }
+ int keyCode = event.getKeyCode();
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_VOICE_ASSIST:
+ handleVoiceAssistKey(event);
+ return;
+ default:
+ break;
+ }
+ if (targetDisplay == InputHalService.DISPLAY_INSTRUMENT_CLUSTER) {
+ handleInstrumentClusterKey(event);
+ } else {
+ handleMainDisplayKey(event);
+ }
+ }
+
+ private void handleVoiceAssistKey(KeyEvent event) {
+ int action = event.getAction();
+ if (action == KeyEvent.ACTION_DOWN) {
+ long now = SystemClock.elapsedRealtime();
+ synchronized (this) {
+ mLastVoiceKeyDownTime = now;
+ }
+ } else if (action == KeyEvent.ACTION_UP) {
+ // if no listener, do not handle long press
+ KeyEventListener listener = null;
+ KeyEventListener shortPressListener = null;
+ KeyEventListener longPressListener = null;
+ long downTime;
+ synchronized (this) {
+ shortPressListener = mVoiceAssitantKeyListener;
+ longPressListener = mLongVoiceAssitantKeyListener;
+ downTime = mLastVoiceKeyDownTime;
+ }
+ if (shortPressListener == null && longPressListener == null) {
+ launchDefaultVoiceAssitantHandler();
+ } else {
+ long duration = SystemClock.elapsedRealtime() - downTime;
+ listener = (duration > VOICE_LONG_PRESS_TIME_MS
+ ? longPressListener : shortPressListener);
+ if (listener != null) {
+ listener.onKeyEvent(event);
+ } else {
+ launchDefaultVoiceAssitantHandler();
+ }
+ }
+ }
+ }
+
+ private void launchDefaultVoiceAssitantHandler() {
+ Log.i(CarLog.TAG_INPUT, "voice key, launch default intent");
+ Intent voiceIntent =
+ new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+ mContext.startActivityAsUser(voiceIntent, null, UserHandle.CURRENT_OR_SELF);
+ }
+
+ private void handleInstrumentClusterKey(KeyEvent event) {
+ KeyEventListener listener = null;
+ synchronized (this) {
+ listener = mInstumentClusterKeyListener;
+ }
+ if (listener == null) {
+ return;
+ }
+ listener.onKeyEvent(event);
+ }
+
+ private void handleMainDisplayKey(KeyEvent event) {
+ int fd;
+ synchronized (this) {
+ fd = mInjectionDeviceFd.getFd();
+ }
+ int action = event.getAction();
+ boolean isDown = (action == KeyEvent.ACTION_DOWN);
+ int keyCode = event.getKeyCode();
+ int r = nativeInjectKeyEvent(fd, keyCode, isDown);
+ if (r != 0) {
+ Log.e(CarLog.TAG_INPUT, "cannot inject key event, failed with:" + r);
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("*Input Service*");
+ writer.println("mInjectionDeviceFd:" + mInjectionDeviceFd);
+ writer.println("mLastVoiceKeyDownTime:" + mLastVoiceKeyDownTime +
+ ",mKeyEventCount:" + mKeyEventCount);
+ }
+
+ private native int nativeInjectKeyEvent(int fd, int keyCode, boolean isDown);
+}
diff --git a/service/src/com/android/car/CarLog.java b/service/src/com/android/car/CarLog.java
index 551b9a7..58cf058 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -19,6 +19,7 @@
public class CarLog {
public static final String TAG_APP_CONTEXT = "CAR.APP_CONTEXT";
public static final String TAG_AUDIO = "CAR.AUDIO";
+ public static final String TAG_CAMERA = "CAR.CAMERA";
public static final String TAG_HAL = "CAR.HAL";
public static final String TAG_HVAC = "CAR.HVAC";
public static final String TAG_INFO = "CAR.INFO";
@@ -29,4 +30,6 @@
public static final String TAG_SERVICE = "CAR.SERVICE";
public static final String TAG_NAV = "CAR.NAV";
public static final String TAG_TEST = "CAR.TEST";
+ public static final String TAG_INPUT = "CAR.INPUT";
+ public static final String TAG_CLUSTER = "CAR.CLUSTER";
}
diff --git a/service/src/com/android/car/CarSensorService.java b/service/src/com/android/car/CarSensorService.java
index 992c033..39dc341 100644
--- a/service/src/com/android/car/CarSensorService.java
+++ b/service/src/com/android/car/CarSensorService.java
@@ -375,13 +375,8 @@
private boolean sensorSupportRate(int sensorType) {
switch (sensorType) {
- case CarSensorManager.SENSOR_TYPE_COMPASS:
case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
case CarSensorManager.SENSOR_TYPE_RPM:
- case CarSensorManager.SENSOR_TYPE_LOCATION:
- case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
- case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE:
- case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
return true;
case CarSensorManager.SENSOR_TYPE_ODOMETER:
case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
@@ -415,9 +410,6 @@
}
String permission = null;
switch (sensorType) {
- case CarSensorManager.SENSOR_TYPE_LOCATION:
- permission = Manifest.permission.ACCESS_FINE_LOCATION;
- break;
case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
permission = Car.PERMISSION_SPEED;
break;
@@ -958,11 +950,7 @@
if (record != null && record.lastEvent != null) {
writer.println("sensor: " + sensor
+ " active: " + record.enabled);
- // only print sensor data which is not related with location
- if (sensor != CarSensorManager.SENSOR_TYPE_LOCATION &&
- sensor != CarSensorManager.SENSOR_TYPE_GPS_SATELLITE) {
- writer.println(" " + record.lastEvent.toString());
- }
+ writer.println(" " + record.lastEvent.toString());
}
SensorListeners listeners = mSensorListeners.get(sensor);
if (listeners != null) {
diff --git a/service/src/com/android/car/CarTestService.java b/service/src/com/android/car/CarTestService.java
index 2752e30..1a983c7 100644
--- a/service/src/com/android/car/CarTestService.java
+++ b/service/src/com/android/car/CarTestService.java
@@ -23,6 +23,7 @@
import com.android.car.hal.VehicleHal;
import com.android.car.vehiclenetwork.IVehicleNetworkHalMock;
import com.android.car.vehiclenetwork.VehicleNetwork;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs;
import com.android.car.vehiclenetwork.VehiclePropValueParcelable;
import java.io.PrintWriter;
@@ -126,6 +127,13 @@
});
}
+ @Override
+ public boolean isPropertySupported(int property) {
+ VehiclePropConfigs configs = VehicleHal.getInstance().getVehicleNetwork().listProperties(
+ property);
+ return configs != null;
+ }
+
public synchronized boolean isInMocking() {
return mInMocking;
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 2166eff..7745a82 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -18,22 +18,29 @@
import android.car.Car;
import android.car.ICar;
-import android.car.ICarConnectionListener;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Log;
+import com.android.car.cluster.CarNavigationService;
+import com.android.car.cluster.InstrumentClusterService;
+import com.android.car.cluster.MediaStatusService;
import com.android.car.hal.VehicleHal;
import com.android.car.pm.CarPackageManagerService;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
-import java.util.Collection;
public class ICarImpl extends ICar.Stub {
+ public static final String INTERNAL_INPUT_SERVICE = "internal_input";
+
+ // load jni for all services here
+ static {
+ System.loadLibrary("jni_car_service");
+ }
+
@GuardedBy("ICarImpl.class")
private static ICarImpl sInstance = null;
@@ -41,26 +48,26 @@
private final VehicleHal mHal;
private final CarPowerManagementService mCarPowerManagementService;
+ private final CarPackageManagerService mCarPackageManagerService;
+ private final CarInputService mCarInputService;
private final CarSensorService mCarSensorService;
private final CarInfoService mCarInfoService;
private final CarAudioService mCarAudioService;
+ private final CarCameraService mCarCameraService;
private final CarHvacService mCarHvacService;
private final CarRadioService mCarRadioService;
private final CarNightService mCarNightService;
private final AppContextService mAppContextService;
- private final CarPackageManagerService mCarPackageManagerService;
private final GarageModeService mGarageModeService;
- private final CarNavigationService mCarNavigationStatusService;
+ private final CarNavigationService mCarNavigationService;
+ private final MediaStatusService mMediaStatusService;
+ private final InstrumentClusterService mInstrumentClusterService;
/** Test only service. Populate it only when necessary. */
@GuardedBy("this")
private CarTestService mCarTestService;
private final CarServiceBase[] mAllServices;
- /** Holds connection listener from client. Only necessary for mocking. */
- private final BinderInterfaceContainer<ICarConnectionListener> mCarConnectionListeners =
- new BinderInterfaceContainer<>(null);
-
public synchronized static ICarImpl getInstance(Context serviceContext) {
if (sInstance == null) {
sInstance = new ICarImpl(serviceContext);
@@ -81,30 +88,39 @@
mContext = serviceContext;
mHal = VehicleHal.getInstance();
mCarPowerManagementService = new CarPowerManagementService(serviceContext);
+ mCarInputService = new CarInputService(serviceContext);
mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService);
mCarInfoService = new CarInfoService(serviceContext);
mAppContextService = new AppContextService(serviceContext);
mCarSensorService = new CarSensorService(serviceContext);
- mCarAudioService = new CarAudioService(serviceContext, mAppContextService);
+ mCarAudioService = new CarAudioService(serviceContext);
mCarHvacService = new CarHvacService(serviceContext);
mCarRadioService = new CarRadioService(serviceContext);
+ mCarCameraService = new CarCameraService(serviceContext);
mCarNightService = new CarNightService(serviceContext);
mCarPackageManagerService = new CarPackageManagerService(serviceContext);
- mCarNavigationStatusService = new CarNavigationService(serviceContext, mAppContextService);
+ mInstrumentClusterService = new InstrumentClusterService(serviceContext);
+ mMediaStatusService = new MediaStatusService(serviceContext, mInstrumentClusterService);
+ mCarNavigationService = new CarNavigationService(
+ serviceContext, mAppContextService, mInstrumentClusterService);
// Be careful with order. Service depending on other service should be inited later.
mAllServices = new CarServiceBase[] {
mCarPowerManagementService,
- mGarageModeService,
mCarPackageManagerService,
+ mCarInputService,
+ mGarageModeService,
mCarInfoService,
mAppContextService,
mCarSensorService,
mCarAudioService,
mCarHvacService,
mCarRadioService,
+ mCarCameraService,
mCarNightService,
- mCarNavigationStatusService,
+ mInstrumentClusterService,
+ mCarNavigationService,
+ mMediaStatusService
};
}
@@ -115,7 +131,6 @@
}
private void release() {
- mCarConnectionListeners.clear();
// release done in opposite order from init
for (int i = mAllServices.length - 1; i >= 0; i--) {
mAllServices[i].release();
@@ -141,25 +156,6 @@
for (CarServiceBase service: mAllServices) {
service.init();
}
- // send disconnect event and connect event to all clients.
- Collection<BinderInterfaceContainer.BinderInterface<ICarConnectionListener>>
- connectionListeners = mCarConnectionListeners.getInterfaces();
- for (BinderInterfaceContainer.BinderInterface<ICarConnectionListener> client :
- connectionListeners) {
- try {
- client.binderInterface.onDisconnected();
- } catch (RemoteException e) {
- //ignore
- }
- }
- for (BinderInterfaceContainer.BinderInterface<ICarConnectionListener> client :
- connectionListeners) {
- try {
- client.binderInterface.onConnected();
- } catch (RemoteException e) {
- //ignore
- }
- }
}
@Override
@@ -175,6 +171,9 @@
return mAppContextService;
case Car.PACKAGE_SERVICE:
return mCarPackageManagerService;
+ case Car.CAMERA_SERVICE:
+ assertCameraPermission(mContext);
+ return mCarCameraService;
case Car.HVAC_SERVICE:
assertHvacPermission(mContext);
return mCarHvacService;
@@ -182,7 +181,7 @@
assertRadioPermission(mContext);
return mCarRadioService;
case Car.CAR_NAVIGATION_SERVICE:
- return mCarNavigationStatusService;
+ return mCarNavigationService;
case Car.TEST_SERVICE: {
assertVehicleHalMockPermission(mContext);
synchronized (this) {
@@ -199,23 +198,23 @@
}
@Override
- public boolean isConnectedToCar() {
- return true; // always connected in embedded
- }
-
- @Override
- public void registerCarConnectionListener(ICarConnectionListener listener) {
- mCarConnectionListeners.addBinder(listener);
- try {
- listener.onConnected();
- } catch (RemoteException e) {
- //ignore
+ public int getCarConnectionType() {
+ if (!isInMocking()) {
+ return Car.CONNECTION_TYPE_EMBEDDED;
+ } else {
+ return Car.CONNECTION_TYPE_EMBEDDED_MOCKING;
}
}
- @Override
- public void unregisterCarConnectionListener(ICarConnectionListener listener) {
- mCarConnectionListeners.removeBinder(listener);
+ public CarServiceBase getCarInternalService(String serviceName) {
+ switch (serviceName) {
+ case INTERNAL_INPUT_SERVICE:
+ return mCarInputService;
+ default:
+ Log.w(CarLog.TAG_SERVICE, "getCarInternalService for unknown service:" +
+ serviceName);
+ return null;
+ }
}
/**
@@ -236,6 +235,14 @@
}
}
+ public static void assertCameraPermission(Context context) {
+ if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_CAMERA)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "requires " + Car.PERMISSION_CAR_CAMERA);
+ }
+ }
+
public static void assertHvacPermission(Context context) {
if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_HVAC)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/service/src/com/android/car/CarNavigationService.java b/service/src/com/android/car/cluster/CarNavigationService.java
similarity index 66%
rename from service/src/com/android/car/CarNavigationService.java
rename to service/src/com/android/car/cluster/CarNavigationService.java
index b061a84..597d10c 100644
--- a/service/src/com/android/car/CarNavigationService.java
+++ b/service/src/com/android/car/cluster/CarNavigationService.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car;
+package com.android.car.cluster;
import android.car.CarAppContextManager;
-import android.car.cluster.NavigationRenderer;
+import android.car.cluster.renderer.NavigationRenderer;
import android.car.navigation.CarNavigationInstrumentCluster;
import android.car.navigation.CarNavigationManager;
import android.car.navigation.ICarNavigation;
@@ -25,9 +25,16 @@
import android.graphics.Bitmap;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import com.android.car.AppContextService;
+import com.android.car.CarLog;
+import com.android.car.CarServiceBase;
+import com.android.car.cluster.InstrumentClusterService.RendererInitializationListener;
+import com.android.car.cluster.renderer.ThreadSafeNavigationRenderer;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -36,20 +43,22 @@
* Service that will push navigation event to navigation renderer in instrument cluster.
*/
public class CarNavigationService extends ICarNavigation.Stub
- implements CarServiceBase {
+ implements CarServiceBase, RendererInitializationListener {
private static final String TAG = CarLog.TAG_NAV;
private final List<CarNavigationEventListener> mListeners = new ArrayList<>();
-
- private CarNavigationInstrumentCluster mInstrumentClusterInfo = null;
private final AppContextService mAppContextService;
private final Context mContext;
- private final boolean mRendererAvailable;
+ private final InstrumentClusterService mInstrumentClusterService;
- public CarNavigationService(Context context, AppContextService appContextService) {
+ private CarNavigationInstrumentCluster mInstrumentClusterInfo = null;
+ private volatile NavigationRenderer mNavigationRenderer;
+
+ public CarNavigationService(Context context, AppContextService appContextService,
+ InstrumentClusterService instrumentClusterService) {
mContext = context;
mAppContextService = appContextService;
- mRendererAvailable = InstrumentClusterRendererLoader.isRendererAvailable(mContext);
+ mInstrumentClusterService = instrumentClusterService;
}
@Override
@@ -57,6 +66,7 @@
Log.d(TAG, "init");
// TODO: we need to obtain this infromation from CarInstrumentClusterService.
mInstrumentClusterInfo = CarNavigationInstrumentCluster.createCluster(1000);
+ mInstrumentClusterService.registerListener(this);
}
@Override
@@ -64,21 +74,22 @@
synchronized(mListeners) {
mListeners.clear();
}
+ mInstrumentClusterService.unregisterListener(this);
}
@Override
public void sendNavigationStatus(int status) {
Log.d(TAG, "sendNavigationStatus, status: " + status);
- if (!checkRendererAvailable()) {
+ if (!isRendererAvailable()) {
return;
}
verifyNavigationContextOwner();
if (status == CarNavigationManager.STATUS_ACTIVE) {
- getNavRenderer().onStartNavigation();
+ mNavigationRenderer.onStartNavigation();
} else if (status == CarNavigationManager.STATUS_INACTIVE
|| status == CarNavigationManager.STATUS_UNAVAILABLE) {
- getNavRenderer().onStopNavigation();
+ mNavigationRenderer.onStopNavigation();
} else {
throw new IllegalArgumentException("Unknown navigation status: " + status);
}
@@ -89,49 +100,47 @@
int event, String road, int turnAngle, int turnNumber, Bitmap image, int turnSide) {
Log.d(TAG, "sendNavigationTurnEvent, event:" + event + ", turnAngle: " + turnAngle + ", "
+ "turnNumber: " + turnNumber + ", " + "turnSide: " + turnSide);
- if (!checkRendererAvailable()) {
+ if (!isRendererAvailable()) {
return;
}
verifyNavigationContextOwner();
- getNavRenderer().onNextTurnChanged(event, road, turnAngle, turnNumber, image, turnSide);
+ mNavigationRenderer.onNextTurnChanged(event, road, turnAngle, turnNumber, image, turnSide);
}
@Override
public void sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds) {
Log.d(TAG, "sendNavigationTurnDistanceEvent, distanceMeters:" + distanceMeters + ", "
+ "timeSeconds: " + timeSeconds);
- if (!checkRendererAvailable()) {
+ if (!isRendererAvailable()) {
return;
}
verifyNavigationContextOwner();
- getNavRenderer().onNextTurnDistanceChanged(distanceMeters, timeSeconds);
+ mNavigationRenderer.onNextTurnDistanceChanged(distanceMeters, timeSeconds);
}
@Override
public boolean registerEventListener(ICarNavigationEventListener listener) {
+ CarNavigationEventListener eventListener;
synchronized(mListeners) {
- if (findClientLocked(listener) == null) {
- CarNavigationEventListener eventListener =
- new CarNavigationEventListener(listener);
- try {
- listener.asBinder().linkToDeath(eventListener, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Adding listener failed.");
- return false;
- }
- mListeners.add(eventListener);
-
- // The new listener needs to be told the instrument cluster parameters.
- try {
- // TODO: onStart and onStop methods might be triggered from vehicle HAL as well.
- eventListener.listener.onInstrumentClusterStart(mInstrumentClusterInfo);
- } catch (RemoteException e) {
- Log.e(TAG, "listener.onStart failed.");
- return false;
- }
+ if (findClientLocked(listener) != null) {
+ return true;
}
+
+ eventListener = new CarNavigationEventListener(listener);
+ try {
+ listener.asBinder().linkToDeath(eventListener, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Adding listener failed.", e);
+ return false;
+ }
+ mListeners.add(eventListener);
+ }
+
+ // The new listener needs to be told the instrument cluster parameters.
+ if (isRendererAvailable()) {
+ return eventListener.onInstrumentClusterStart(mInstrumentClusterInfo);
}
return true;
}
@@ -188,6 +197,19 @@
return null;
}
+ @Override
+ public void onRendererInitSucceeded() {
+ Log.d(TAG, "onRendererInitSucceeded");
+ mNavigationRenderer = ThreadSafeNavigationRenderer.createFor(
+ Looper.getMainLooper(),
+ mInstrumentClusterService.getNavigationRenderer());
+
+ if (isRendererAvailable()) {
+ for (CarNavigationEventListener listener : mListeners) {
+ listener.onInstrumentClusterStart(mInstrumentClusterInfo);
+ }
+ }
+ }
private class CarNavigationEventListener implements IBinder.DeathRecipient {
final ICarNavigationEventListener listener;
@@ -201,6 +223,17 @@
listener.asBinder().unlinkToDeath(this, 0);
removeClient(this);
}
+
+ /** Returns true if event sent successfully */
+ public boolean onInstrumentClusterStart(CarNavigationInstrumentCluster clusterInfo) {
+ try {
+ listener.onInstrumentClusterStart(clusterInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onInstrumentClusterStart for listener: " + listener, e);
+ return false;
+ }
+ return true;
+ }
}
@Override
@@ -208,15 +241,11 @@
// TODO Auto-generated method stub
}
- private NavigationRenderer getNavRenderer() {
- // TODO: implement InstrumentClusterService that will abstract cluster renderer.
- return InstrumentClusterRendererLoader.getRenderer(mContext).getNavigationRenderer();
- }
-
- private boolean checkRendererAvailable() {
- if (!mRendererAvailable) {
+ private boolean isRendererAvailable() {
+ boolean available = mNavigationRenderer != null;
+ if (!available) {
Log.w(TAG, "Instrument cluster renderer is not available.");
}
- return mRendererAvailable;
+ return available;
}
}
diff --git a/service/src/com/android/car/cluster/InstrumentClusterPresentation.java b/service/src/com/android/car/cluster/InstrumentClusterPresentation.java
new file mode 100644
index 0000000..53c73f8
--- /dev/null
+++ b/service/src/com/android/car/cluster/InstrumentClusterPresentation.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.view.Display;
+import android.view.WindowManager;
+
+/**
+ * Presentation class.
+ */
+public class InstrumentClusterPresentation extends Presentation {
+ public InstrumentClusterPresentation(Context outerContext, Display display) {
+ super(outerContext, display);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ }
+}
diff --git a/service/src/com/android/car/InstrumentClusterRendererLoader.java b/service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java
similarity index 70%
rename from service/src/com/android/car/InstrumentClusterRendererLoader.java
rename to service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java
index c892cb6..e6742a6 100644
--- a/service/src/com/android/car/InstrumentClusterRendererLoader.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java
@@ -13,15 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car;
+package com.android.car.cluster;
-import android.car.cluster.InstrumentClusterRenderer;
+import android.car.cluster.renderer.InstrumentClusterRenderer;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.hardware.display.DisplayManager;
import android.util.Log;
-import android.view.Display;
+
+import com.android.car.CarLog;
+import com.android.car.R;
import dalvik.system.PathClassLoader;
@@ -36,64 +37,43 @@
private final static String sCreateRendererMethod = "createRenderer";
- private static InstrumentClusterRenderer sRenderer;
- private static Object sync = new Object();
-
/**
- * Load {@link InstrumentClusterRenderer} from separate APK and cache it. Subsequent calls to
- * this function will return cached reference to the renderer.
- */
- public static InstrumentClusterRenderer getRenderer(Context context) {
- synchronized (sync) {
- if (sRenderer == null) {
- Display display = getInstrumentClusterDisplay(context);
- if (display != null) {
- createRenderer(context, display);
- } else {
- Log.e(TAG, "Instrument cluster display not found.");
- }
- }
- }
- return sRenderer;
- }
-
- /**
- * Returns true if instrument cluster renderer installed and secondary display is available.
+ * Returns true if instrument cluster renderer installed.
*/
public static boolean isRendererAvailable(Context context) {
PackageManager packageManager = context.getPackageManager();
try {
packageManager.getPackageInfo(getRendererPackageName(context), 0);
- return getInstrumentClusterDisplay(context) != null;
+ return true;
} catch (NameNotFoundException e) {
return false;
}
}
- /** To prevent instantiation of a singleton class. */
- private InstrumentClusterRendererLoader() {}
-
- private static void createRenderer(Context context, Display display) {
- String packageName = getRendererPackageName(context);
-
+ /** Dynamically load renderer APK and creates {@link InstrumentClusterRenderer}. */
+ public static InstrumentClusterRenderer createRenderer(Context context) {
+ final String packageName = getRendererPackageName(context);
try {
- sRenderer = load(context, packageName, getRendererFactoryClassName(context));
+ return load(context, packageName, getRendererFactoryClassName(context));
} catch (Exception e) {
Log.e(TAG, "Failed to load renderer class: " + e.getMessage(), e);
throw new RuntimeException(e);
}
-
- sRenderer.onCreate(createPackageContext(context, packageName), display);
- sRenderer.onStart();
}
+ public static Context createRendererPackageContext(Context context) {
+ return createPackageContext(context, getRendererPackageName(context));
+ }
+
+ /** To prevent instantiation of a singleton class. */
+ private InstrumentClusterRendererLoader() {}
+
/**
* Creates package context for given package name. It is necessary to get renderer's context
* so appropriate resources will be loaded in the renderer code.
*/
private static Context createPackageContext(Context currentContext, String packageName) {
try {
- // TODO: check APK certificate.
return currentContext.createPackageContext(packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
} catch (NameNotFoundException e) {
@@ -102,22 +82,6 @@
}
}
- private static Display getInstrumentClusterDisplay(Context context) {
- DisplayManager displayManager = context.getSystemService(DisplayManager.class);
- Display[] displays = displayManager.getDisplays();
-
- Log.d(TAG, "There are currently " + displays.length + " displays connected.");
- for (Display display : displays) {
- Log.d(TAG, " " + display);
- }
-
- if (displays.length > 1) {
- // TODO: assuming that secondary display is instrument cluster. Put this into settings?
- return displays[1];
- }
- return null;
- }
-
/** Returns instrument cluster renderer or null if renderer package is not found */
private static InstrumentClusterRenderer load(Context context, String packageName,
String factoryClassName) throws Exception {
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
new file mode 100644
index 0000000..a3a8be6
--- /dev/null
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster;
+
+import static com.android.car.cluster.InstrumentClusterRendererLoader.createRenderer;
+import static com.android.car.cluster.InstrumentClusterRendererLoader.createRendererPackageContext;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.car.cluster.renderer.InstrumentClusterRenderer;
+import android.car.cluster.renderer.MediaRenderer;
+import android.car.cluster.renderer.NavigationRenderer;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.car.CarLog;
+import com.android.car.CarServiceBase;
+import com.android.car.CarServiceUtils;
+import com.android.car.R;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Service responsible for interaction with car's instrument cluster.
+ *
+ * @hide
+ */
+@SystemApi
+public class InstrumentClusterService implements CarServiceBase {
+
+ private static final String TAG = CarLog.TAG_CLUSTER + "."
+ + InstrumentClusterService.class.getSimpleName();
+
+ private static final int RENDERER_INIT_TIMEOUT_MS = 10 * 1000;
+ private final static int MSG_TIMEOUT = 1;
+
+ private final Context mContext;
+ private final Object mHalSync = new Object();
+ private final CopyOnWriteArrayList<RendererInitializationListener>
+ mRendererInitializationListeners = new CopyOnWriteArrayList<>();
+
+ private InstrumentClusterRenderer mRenderer;
+
+ private final Handler mHandler;
+
+ public InstrumentClusterService(Context context) {
+ mContext = context;
+ mHandler = new TimeoutHandler();
+ }
+
+ public interface RendererInitializationListener {
+ void onRendererInitSucceeded();
+ }
+
+ @Override
+ public void init() {
+ Log.d(TAG, "init");
+
+ if (getInstrumentClusterType() == InstrumentClusterType.GRAPHICS) {
+ Display display = getInstrumentClusterDisplay(mContext);
+ boolean rendererFound = InstrumentClusterRendererLoader.isRendererAvailable(mContext);
+
+ if (display != null && rendererFound) {
+ initRendererOnMainThread(display);
+ } else {
+ throw new RuntimeException("Failed to initialize InstrumentClusterRenderer"
+ + ", renderer found: " + rendererFound
+ + ", secondary display: " + (display != null));
+ }
+ }
+
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), RENDERER_INIT_TIMEOUT_MS);
+ }
+
+ private void initRendererOnMainThread(final Display display) {
+ CarServiceUtils.runOnMain(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "initRendererOnMainThread");
+ try {
+ InstrumentClusterPresentation presentation =
+ new InstrumentClusterPresentation(mContext, display);
+
+ ViewGroup rootView = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.instrument_cluster, null);
+
+ presentation.setContentView(rootView);
+ InstrumentClusterRenderer renderer = createRenderer(mContext);
+ renderer.onCreate(createRendererPackageContext(mContext));
+ View rendererView = renderer.onCreateView(null);
+ renderer.initialize();
+ rootView.addView(rendererView);
+ presentation.show();
+ initUiDone(renderer);
+ } catch (Exception e) {
+ Log.e(TAG, e.getMessage(), e);
+ throw e;
+ }
+ }
+ });
+ }
+
+ private void initUiDone(final InstrumentClusterRenderer renderer) {
+ Log.d(TAG, "initUiDone");
+ mHandler.removeMessages(MSG_TIMEOUT);
+
+ // Call listeners in service thread.
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ mRenderer = renderer;
+
+ for (RendererInitializationListener listener : mRendererInitializationListeners) {
+ listener.onRendererInitSucceeded();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void release() {
+ Log.d(TAG, "release");
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ // TODO
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ InstrumentClusterType.NONE,
+ InstrumentClusterType.METADATA,
+ InstrumentClusterType.GRAPHICS
+ })
+ public @interface InstrumentClusterType {
+ /**
+ * For use privately in this class.
+ * @hide
+ */
+ int UNDEFINED = -1;
+
+ /** Access to instrument cluster is not available */
+ int NONE = 0;
+
+ /** Access to instrument cluster through vehicle HAL using meta-data. */
+ int METADATA = 1;
+
+ /** Access instrument cluster as a secondary display. */
+ int GRAPHICS = 2;
+ }
+
+ @InstrumentClusterType private int mClusterType = InstrumentClusterType.UNDEFINED;
+
+ public boolean isInstrumentClusterAvailable() {
+ return mClusterType != InstrumentClusterType.NONE
+ && mClusterType != InstrumentClusterType.UNDEFINED;
+ }
+
+ public int getInstrumentClusterType() {
+ if (mClusterType == InstrumentClusterType.UNDEFINED) {
+ synchronized (mHalSync) {
+ // TODO: need to pull this information from the HAL
+ mClusterType = getInstrumentClusterDisplay(mContext) != null
+ ? InstrumentClusterType.GRAPHICS : InstrumentClusterType.NONE;
+ }
+ }
+ return mClusterType;
+ }
+
+ @Nullable
+ public MediaRenderer getMediaRenderer() {
+ return mRenderer != null ? mRenderer.getMediaRenderer() : null;
+ }
+
+ @Nullable
+ public NavigationRenderer getNavigationRenderer() {
+ return mRenderer != null ? mRenderer.getNavigationRenderer() : null;
+ }
+
+ public void registerListener(RendererInitializationListener listener) {
+ mRendererInitializationListeners.add(listener);
+ }
+
+ public void unregisterListener(RendererInitializationListener listener) {
+ mRendererInitializationListeners.remove(listener);
+ }
+
+ private static Display getInstrumentClusterDisplay(Context context) {
+ DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ Display[] displays = displayManager.getDisplays();
+
+ Log.d(TAG, "There are currently " + displays.length + " displays connected.");
+ for (Display display : displays) {
+ Log.d(TAG, " " + display);
+ }
+
+ if (displays.length > 1) {
+ // TODO: assuming that secondary display is instrument cluster. Put this into settings?
+ return displays[1];
+ }
+ return null;
+ }
+
+ private void runOnServiceThread(final Runnable runnable) {
+ if (Looper.myLooper() == mHandler.getLooper()) {
+ runnable.run();
+ } else {
+ mHandler.post(runnable);
+ }
+ }
+
+ private static class TimeoutHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_TIMEOUT) {
+ throw new RuntimeException("Renderer initialization timeout.");
+ } else {
+ super.handleMessage(msg);
+ }
+ }
+ }
+}
diff --git a/service/src/com/android/car/cluster/MediaStatusService.java b/service/src/com/android/car/cluster/MediaStatusService.java
new file mode 100644
index 0000000..e4d0306
--- /dev/null
+++ b/service/src/com/android/car/cluster/MediaStatusService.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster;
+
+import android.car.cluster.renderer.MediaRenderer;
+import android.content.Context;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
+import android.media.session.PlaybackState;
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.car.CarLog;
+import com.android.car.CarServiceBase;
+import com.android.car.cluster.InstrumentClusterService.RendererInitializationListener;
+import com.android.car.cluster.renderer.ThreadSafeMediaRenderer;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * Reports current media status to instrument cluster renderer.
+ */
+public class MediaStatusService implements CarServiceBase, RendererInitializationListener {
+
+ private final static String TAG = CarLog.TAG_CLUSTER
+ + "." + MediaStatusService.class.getSimpleName();
+
+ private final Context mContext;
+ private final MediaListener mMediaListener;
+ private volatile MediaRenderer mMediaRenderer;
+ private InstrumentClusterService mInstrumentClusterService;
+ private MediaController mPrimaryMediaController;
+ private OnActiveSessionsChangedListener mActiveSessionsChangedListener;
+ private MediaSessionManager mMediaSessionManager;
+
+ public MediaStatusService(Context context, InstrumentClusterService instrumentClusterService) {
+ mContext = context;
+ mInstrumentClusterService = instrumentClusterService;
+ instrumentClusterService.registerListener(this);
+ mMediaListener = new MediaListener(MediaStatusService.this);
+ }
+
+ @Override
+ public void init() {
+ Log.d(TAG, "init");
+
+ if (!mInstrumentClusterService.isInstrumentClusterAvailable()) {
+ Log.d(TAG, "Instrument cluster is not available.");
+ return;
+ }
+
+ mActiveSessionsChangedListener = new OnActiveSessionsChangedListener() {
+ @Override
+ public void onActiveSessionsChanged(List<MediaController> controllers) {
+ MediaStatusService.this.onActiveSessionsChanged(controllers);
+ }
+ };
+ mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
+ mMediaSessionManager.addOnActiveSessionsChangedListener(
+ mActiveSessionsChangedListener, null);
+
+ onActiveSessionsChanged(mMediaSessionManager.getActiveSessions(null));
+ }
+
+ private void onActiveSessionsChanged(List<MediaController> controllers) {
+ Log.d(TAG, "onActiveSessionsChanged, controllers found: " + controllers.size());
+ MediaController newPrimaryController = null;
+ if (controllers.size() > 0) {
+ newPrimaryController = controllers.get(0);
+ if (mPrimaryMediaController == newPrimaryController) {
+ // Primary media controller has not been changed.
+ return;
+ }
+ }
+
+ releasePrimaryMediaController();
+
+ if (newPrimaryController != null) {
+ mPrimaryMediaController = newPrimaryController;
+ mPrimaryMediaController.registerCallback(mMediaListener);
+ }
+ updateRendererMediaStatusIfAvailable();
+
+ for (MediaController m : controllers) {
+ Log.d(TAG, m + ": " + m.getPackageName());
+ }
+ }
+
+ @Override
+ public void release() {
+ releasePrimaryMediaController();
+ if (mActiveSessionsChangedListener != null) {
+ mMediaSessionManager.removeOnActiveSessionsChangedListener(
+ mActiveSessionsChangedListener);
+ mActiveSessionsChangedListener = null;
+ }
+ mMediaSessionManager = null;
+ }
+
+ private void releasePrimaryMediaController() {
+ if (mPrimaryMediaController != null) {
+ mPrimaryMediaController.unregisterCallback(mMediaListener);
+ mPrimaryMediaController = null;
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ // TODO
+ }
+
+ @Override
+ public void onRendererInitSucceeded() {
+ Log.d(TAG, "onRendererInitSucceeded");
+ mMediaRenderer = ThreadSafeMediaRenderer.createFor(
+ Looper.getMainLooper(),
+ mInstrumentClusterService.getMediaRenderer());
+
+ updateRendererMediaStatusIfAvailable();
+ }
+
+ private void updateRendererMediaStatusIfAvailable() {
+ if (isRendererAvailable()) {
+ mMediaRenderer.onMetadataChanged(
+ mPrimaryMediaController == null ? null : mPrimaryMediaController.getMetadata());
+ mMediaRenderer.onPlaybackStateChanged(
+ mPrimaryMediaController == null
+ ? null : mPrimaryMediaController.getPlaybackState());
+ }
+ }
+
+ private boolean isRendererAvailable() {
+ boolean available = mMediaRenderer != null;
+ if (!available) {
+ Log.w(TAG, "Media renderer is not available.");
+ }
+ return available;
+ }
+
+ private void onPlaybackStateChanged(PlaybackState state) {
+ if (isRendererAvailable()) {
+ mMediaRenderer.onPlaybackStateChanged(state);
+ }
+ }
+
+ private void onMetadataChanged(MediaMetadata metadata) {
+ if (isRendererAvailable()) {
+ mMediaRenderer.onMetadataChanged(metadata);
+ }
+ }
+
+ private static class MediaListener extends MediaController.Callback {
+ private final WeakReference<MediaStatusService> mServiceRef;
+
+ MediaListener(MediaStatusService service) {
+ mServiceRef = new WeakReference<>(service);
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ MediaStatusService service = mServiceRef.get();
+ if (service != null) {
+ service.onPlaybackStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ MediaStatusService service = mServiceRef.get();
+ if (service != null) {
+ service.onMetadataChanged(metadata);
+ }
+ }
+ }
+}
diff --git a/service/src/com/android/car/cluster/renderer/RendererHandler.java b/service/src/com/android/car/cluster/renderer/RendererHandler.java
new file mode 100644
index 0000000..b999dde
--- /dev/null
+++ b/service/src/com/android/car/cluster/renderer/RendererHandler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster.renderer;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Abstract {@link Handler} class that holds reference to a renderer object.
+ */
+public abstract class RendererHandler<T> extends Handler {
+
+ private final WeakReference<T> mRendererRef;
+
+ RendererHandler(Looper looper, T renderer) {
+ super(looper);
+ mRendererRef = new WeakReference<>(renderer);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ T renderer = mRendererRef.get();
+ if (renderer != null) {
+ handleMessage(msg, renderer);
+ }
+ }
+
+ public abstract void handleMessage(Message msg, T renderer);
+}
diff --git a/service/src/com/android/car/cluster/renderer/ThreadSafeMediaRenderer.java b/service/src/com/android/car/cluster/renderer/ThreadSafeMediaRenderer.java
new file mode 100644
index 0000000..fcf3989
--- /dev/null
+++ b/service/src/com/android/car/cluster/renderer/ThreadSafeMediaRenderer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster.renderer;
+
+import android.car.cluster.renderer.MediaRenderer;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * A wrapper over {@link MediaRenderer} that runs all its methods in the context of provided looper.
+ * It is guaranteed that all calls will be invoked in order they were called.
+ */
+public class ThreadSafeMediaRenderer extends MediaRenderer {
+ private final Handler mHandler;
+
+ private final static int MSG_PLAYBACK_STATE_CHANGED = 1;
+ private final static int MSG_METADATA_CHANGED = 2;
+
+ public static MediaRenderer createFor(Looper looper, MediaRenderer renderer) {
+ return renderer != null ? new ThreadSafeMediaRenderer(renderer, looper) : null;
+ }
+
+ private ThreadSafeMediaRenderer(MediaRenderer mediaRenderer, Looper looper) {
+ mHandler = new MediaRendererHandler(looper, mediaRenderer);
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackState playbackState) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_PLAYBACK_STATE_CHANGED, playbackState));
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_METADATA_CHANGED, metadata));
+ }
+
+ private static class MediaRendererHandler extends RendererHandler<MediaRenderer> {
+
+ MediaRendererHandler(Looper looper, MediaRenderer renderer) {
+ super(looper, renderer);
+ }
+
+ @Override
+ public void handleMessage(Message msg, MediaRenderer renderer) {
+ switch (msg.what) {
+ case MSG_PLAYBACK_STATE_CHANGED:
+ renderer.onPlaybackStateChanged((PlaybackState) msg.obj);
+ break;
+ case MSG_METADATA_CHANGED:
+ renderer.onMetadataChanged((MediaMetadata) msg.obj);
+ break;
+ default:
+ throw new IllegalArgumentException("Msg: " + msg.what);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/service/src/com/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java b/service/src/com/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
new file mode 100644
index 0000000..33dde7a
--- /dev/null
+++ b/service/src/com/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.cluster.renderer;
+
+import android.annotation.Nullable;
+import android.car.cluster.renderer.NavigationRenderer;
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * A wrapper over {@link NavigationRenderer} that runs all its methods in the context of provided
+ * looper. It is guaranteed that all calls will be invoked in order they were called.
+ */
+public class ThreadSafeNavigationRenderer extends NavigationRenderer {
+
+ private final Handler mHandler;
+
+ private final static int MSG_NAV_START = 1;
+ private final static int MSG_NAV_STOP = 2;
+ private final static int MSG_NAV_NEXT_TURN = 3;
+ private final static int MSG_NAV_NEXT_TURN_DISTANCE = 4;
+
+ /** Creates thread-safe {@link NavigationRenderer}. Returns null if renderer == null */
+ @Nullable
+ public static NavigationRenderer createFor(Looper looper, NavigationRenderer renderer) {
+ return renderer == null ? null : new ThreadSafeNavigationRenderer(looper, renderer);
+ }
+
+ private ThreadSafeNavigationRenderer(Looper looper, NavigationRenderer renderer) {
+ mHandler = new NavigationRendererHandler(looper, renderer);
+ }
+
+ @Override
+ public void onStartNavigation() {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_START));
+ }
+
+ @Override
+ public void onStopNavigation() {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_STOP));
+ }
+
+ @Override
+ public void onNextTurnChanged(int event, String road, int turnAngle, int turnNumber,
+ Bitmap image, int turnSide) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_NEXT_TURN,
+ new NextTurn(event, road, turnAngle, turnNumber, image, turnSide)));
+ }
+
+ @Override
+ public void onNextTurnDistanceChanged(int distanceMeters, int timeSeconds) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_NAV_NEXT_TURN_DISTANCE, distanceMeters, timeSeconds));
+ }
+
+ private static class NavigationRendererHandler extends RendererHandler<NavigationRenderer> {
+
+ NavigationRendererHandler(Looper looper, NavigationRenderer renderer) {
+ super(looper, renderer);
+ }
+
+ @Override
+ public void handleMessage(Message msg, NavigationRenderer renderer) {
+
+ switch (msg.what) {
+ case MSG_NAV_START:
+ renderer.onStartNavigation();
+ break;
+ case MSG_NAV_STOP:
+ renderer.onStopNavigation();
+ break;
+ case MSG_NAV_NEXT_TURN:
+ NextTurn nt = (NextTurn) msg.obj;
+ renderer.onNextTurnChanged(nt.event, nt.road, nt.turnAngle, nt.turnNumber,
+ nt.bitmap, nt.turnSide);
+ break;
+ case MSG_NAV_NEXT_TURN_DISTANCE:
+ renderer.onNextTurnDistanceChanged(msg.arg1, msg.arg2);
+ break;
+ default:
+ throw new IllegalArgumentException("Msg: " + msg.what);
+ }
+ }
+ }
+
+ private static class NextTurn {
+ private final int event;
+ private final String road;
+ private final int turnAngle;
+ private final int turnNumber;
+ private final Bitmap bitmap;
+ private final int turnSide;
+
+ NextTurn(int event, String road, int turnAngle, int turnNumber, Bitmap bitmap,
+ int turnSide) {
+ this.event = event;
+ this.road = road;
+ this.turnAngle = turnAngle;
+ this.turnNumber = turnNumber;
+ this.bitmap = bitmap;
+ this.turnSide = turnSide;
+ }
+ }
+}
diff --git a/service/src/com/android/car/hal/AudioHalService.java b/service/src/com/android/car/hal/AudioHalService.java
index 68c4d11..5208787 100644
--- a/service/src/com/android/car/hal/AudioHalService.java
+++ b/service/src/com/android/car/hal/AudioHalService.java
@@ -19,6 +19,7 @@
import android.util.Log;
import com.android.car.AudioRoutingPolicy;
+import com.android.car.CarAudioAttributesUtil;
import com.android.car.CarLog;
import com.android.car.vehiclenetwork.VehicleNetwork;
import com.android.car.vehiclenetwork.VehicleNetworkConsts;
@@ -99,6 +100,31 @@
public static final int FOCUS_STATE_ARRAY_INDEX_STREAMS = 1;
public static final int FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS = 2;
+ public static final int AUDIO_CONTEXT_MUSIC_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG;
+ public static final int AUDIO_CONTEXT_NAVIGATION_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG;
+ public static final int AUDIO_CONTEXT_VOICE_COMMAND_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG;
+ public static final int AUDIO_CONTEXT_CALL_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG;
+ public static final int AUDIO_CONTEXT_ALARM_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG;
+ public static final int AUDIO_CONTEXT_NOTIFICATION_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG;
+ public static final int AUDIO_CONTEXT_UNKNOWN_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG;
+ public static final int AUDIO_CONTEXT_SAFETY_ALERT_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG;
+ public static final int AUDIO_CONTEXT_RADIO_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
+ public static final int AUDIO_CONTEXT_CD_ROM_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG;
+ public static final int AUDIO_CONTEXT_AUX_AUDIO_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG;
+ public static final int AUDIO_CONTEXT_SYSTEM_SOUND_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
+
public interface AudioHalListener {
/**
* Audio focus change from car.
@@ -168,19 +194,24 @@
for (int i = 0; i < policy.getPhysicalStreamsCount(); i++) {
policyToSet[VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_STREAM] =
i;
- int streams = 0;
+ int contexts = 0;
for (int logicalStream : policy.getLogicalStreamsForPhysicalStream(i)) {
- streams |= logicalStreamToHalStreamType(logicalStream);
+ contexts |= logicalStreamToHalContextType(logicalStream);
}
policyToSet[VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_CONTEXTS]
- = streams;
+ = contexts;
vn.setIntVectorProperty(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY,
policyToSet);
}
}
- private static int logicalStreamToHalStreamType(int logicalStream) {
+ /**
+ * Convert car audio manager stream type (usage) into audio context type.
+ */
+ public static int logicalStreamToHalContextType(int logicalStream) {
switch (logicalStream) {
+ case CarAudioManager.CAR_AUDIO_USAGE_RADIO:
+ return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
case CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL:
return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG;
case CarAudioManager.CAR_AUDIO_USAGE_MUSIC:
@@ -196,21 +227,25 @@
case CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT:
return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG;
case CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND:
- return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND;
+ return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
case CarAudioManager.CAR_AUDIO_USAGE_DEFAULT:
return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG;
+ case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_BOTTOM:
+ case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY:
+ // internal tag not associated with any stream
+ return 0;
default:
Log.w(CarLog.TAG_AUDIO, "Unknown logical stream:" + logicalStream);
return 0;
}
}
- public void requestAudioFocusChange(int request, int streams) {
- requestAudioFocusChange(request, streams, VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+ public void requestAudioFocusChange(int request, int streams, int audioContexts) {
+ requestAudioFocusChange(request, streams, VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG, audioContexts);
}
- public void requestAudioFocusChange(int request, int streams, int extFocus) {
- int[] payload = { request, streams, extFocus };
+ public void requestAudioFocusChange(int request, int streams, int extFocus, int audioContexts) {
+ int[] payload = { request, streams, extFocus, audioContexts };
mVehicleHal.getVehicleNetwork().setIntVectorProperty(
VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, payload);
}
diff --git a/service/src/com/android/car/hal/InputHalService.java b/service/src/com/android/car/hal/InputHalService.java
new file mode 100644
index 0000000..70c7899
--- /dev/null
+++ b/service/src/com/android/car/hal/InputHalService.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.hal;
+
+import android.util.Log;
+import android.view.KeyEvent;
+
+import com.android.car.CarLog;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleDisplay;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleHwKeyInputAction;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+public class InputHalService extends HalServiceBase {
+
+ public static final int DISPLAY_MAIN = VehicleDisplay.VEHICLE_DISPLAY_MAIN;
+ public static final int DISPLAY_INSTRUMENT_CLUSTER =
+ VehicleDisplay.VEHICLE_DISPLAY_INSTRUMENT_CLUSTER;
+
+ public interface InputListener {
+ void onKeyEvent(KeyEvent event, int targetDisplay);
+ }
+
+ private static final boolean DBG = false;
+
+ private boolean mKeyInputSupported = false;
+ private InputListener mListener;
+
+ public void setInputListener(InputListener listener) {
+ synchronized (this) {
+ if (!mKeyInputSupported) {
+ Log.w(CarLog.TAG_INPUT, "input listener set while key input not supported");
+ return;
+ }
+ mListener = listener;
+ }
+ VehicleHal.getInstance().subscribeProperty(this,
+ VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT, 0);
+ }
+
+ public synchronized boolean isKeyInputSupported() {
+ return mKeyInputSupported;
+ }
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public void release() {
+ synchronized (this) {
+ mListener = null;
+ mKeyInputSupported = false;
+ }
+ }
+
+ @Override
+ public List<VehiclePropConfig> takeSupportedProperties(List<VehiclePropConfig> allProperties) {
+ List<VehiclePropConfig> supported = new LinkedList<VehiclePropConfig>();
+ for (VehiclePropConfig p: allProperties) {
+ if (p.getProp() == VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT) {
+ supported.add(p);
+ synchronized (this) {
+ mKeyInputSupported = true;
+ }
+ }
+ }
+ return supported;
+ }
+
+ @Override
+ public void handleHalEvents(List<VehiclePropValue> values) {
+ InputListener listener = null;
+ synchronized (this) {
+ listener = mListener;
+ }
+ if (listener == null) {
+ Log.w(CarLog.TAG_INPUT, "Input event while listener is null");
+ return;
+ }
+ for (VehiclePropValue v : values) {
+ if (v.getProp() != VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT) {
+ Log.e(CarLog.TAG_INPUT, "Wrong event dispatched, prop:0x" +
+ Integer.toHexString(v.getProp()));
+ continue;
+ }
+ int action = (v.getInt32Values(0) ==
+ VehicleHwKeyInputAction.VEHICLE_HW_KEY_INPUT_ACTION_DOWN) ?
+ KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
+ int code = v.getInt32Values(1);
+ int display = v.getInt32Values(2);
+ if (DBG) {
+ Log.i(CarLog.TAG_INPUT, "hal event code:" + code + ",action:" + action +
+ ",display:" + display);
+ }
+ KeyEvent event = new KeyEvent(action, code);
+ listener.onKeyEvent(event, display);
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("*Input HAL*");
+ writer.println("mKeyInputSupported:" + mKeyInputSupported);
+ }
+
+}
diff --git a/service/src/com/android/car/hal/PowerHalService.java b/service/src/com/android/car/hal/PowerHalService.java
index 899fbe3..8cf412a 100644
--- a/service/src/com/android/car/hal/PowerHalService.java
+++ b/service/src/com/android/car/hal/PowerHalService.java
@@ -332,6 +332,8 @@
@Override
public void dump(PrintWriter writer) {
- // TODO Auto-generated method stub
+ writer.println("*Power HAL*");
+ writer.println("isPowerStateSupported:" + isPowerStateSupported() +
+ ",isDeepSleepAllowed:" + isDeepSleepAllowed());
}
}
diff --git a/service/src/com/android/car/hal/SensorHalService.java b/service/src/com/android/car/hal/SensorHalService.java
index 05e7571..6ea7b15 100644
--- a/service/src/com/android/car/hal/SensorHalService.java
+++ b/service/src/com/android/car/hal/SensorHalService.java
@@ -113,7 +113,8 @@
switch (property) {
// boolean
case VehicleNetworkConsts.VEHICLE_PROPERTY_NIGHT_MODE:
- case VehicleNetworkConsts.VEHICLE_PROPERTY_PARKING_BRAKE_ON: {
+ case VehicleNetworkConsts.VEHICLE_PROPERTY_PARKING_BRAKE_ON:
+ case VehicleNetworkConsts.VEHICLE_PROPERTY_FUEL_LEVEL_LOW: {
if (DBG_EVENTS) {
Log.i(CarLog.TAG_SENSOR, "boolean event, property:" +
Integer.toHexString(property) + " value:" + v.getInt32Values(0));
@@ -241,6 +242,8 @@
return CarSensorManager.SENSOR_TYPE_PARKING_BRAKE;
case VehicleNetworkConsts.VEHICLE_PROPERTY_DRIVING_STATUS:
return CarSensorManager.SENSOR_TYPE_DRIVING_STATUS;
+ case VehicleNetworkConsts.VEHICLE_PROPERTY_FUEL_LEVEL_LOW:
+ return CarSensorManager.SENSOR_TYPE_FUEL_LEVEL;
default:
return SENSOR_TYPE_INVALD;
}
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index d1d0789..ef0e6c7 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -88,6 +88,7 @@
private final RadioHalService mRadioHal;
private final PowerHalService mPowerHal;
private final HvacHalService mHvacHal;
+ private final InputHalService mInputHal;
/** stores handler for each HAL property. Property events are sent to handler. */
private final SparseArray<HalServiceBase> mPropertyHandlers = new SparseArray<HalServiceBase>();
@@ -107,13 +108,16 @@
mAudioHal = new AudioHalService(this);
mRadioHal = new RadioHalService(this);
mHvacHal = new HvacHalService(this);
+ mInputHal = new InputHalService();
mAllServices = new HalServiceBase[] {
mPowerHal,
mAudioHal,
mHvacHal,
mInfoHal,
mSensorHal,
- mRadioHal };
+ mRadioHal,
+ mInputHal
+ };
mVehicleNetwork = VehicleNetwork.createVehicleNetwork(this, mHandlerThread.getLooper());
}
@@ -129,6 +133,7 @@
mAudioHal = audioHal;
mRadioHal = radioHal;
mHvacHal = hvacHal;
+ mInputHal = null;
mAllServices = null;
mVehicleNetwork = vehicleNetwork;
}
@@ -215,24 +220,8 @@
return mHvacHal;
}
- public void updateAppContext(boolean navigationActive, boolean voiceCommandActive,
- boolean callActive) {
- synchronized (this) {
- VehiclePropConfig config = mUnclaimedProperties.get(
- VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_CONTEXT);
- if (config == null) {
- return; // not supported
- }
- }
- int currentContext =
- (navigationActive ?
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG : 0) |
- (voiceCommandActive ?
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG : 0) |
- (callActive ?
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG : 0);
- mVehicleNetwork.setIntProperty(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_CONTEXT,
- currentContext);
+ public InputHalService getInputHal() {
+ return mInputHal;
}
private void assertServiceOwnerLocked(HalServiceBase service, int property) {
diff --git a/tests/car_activity_test_app/Android.mk b/tests/EmbeddedKitchenSinkApp/Android.mk
similarity index 76%
rename from tests/car_activity_test_app/Android.mk
rename to tests/EmbeddedKitchenSinkApp/Android.mk
index 0b9add5..e160e03 100644
--- a/tests/car_activity_test_app/Android.mk
+++ b/tests/EmbeddedKitchenSinkApp/Android.mk
@@ -22,15 +22,18 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := CarActivityTestApp
+LOCAL_PACKAGE_NAME := EmbeddedKitchenSinkApp
-LOCAL_MODULE_TAGS := tests
+LOCAL_AAPT_FLAGS := --auto-add-overlay
-# When built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_ENABLED := disabled
-include packages/services/Car/car-support-lib/car-support.mk
+LOCAL_STATIC_JAVA_LIBRARIES += car-systemtest android.support.car
+
+LOCAL_JAVA_LIBRARIES += android.car
include $(BUILD_PACKAGE)
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
new file mode 100644
index 0000000..aed497f
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="com.google.android.car.kitchensink" >
+ <uses-sdk
+ android:minSdkVersion="22"
+ android:targetSdkVersion='23'/>
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+ <uses-permission android:name="android.car.permission.CAR_SPEED" />
+ <uses-permission android:name="android.car.permission.CAR_HVAC" />
+ <uses-permission android:name="android.car.permission.CAR_MOCK_VEHICLE_HAL" />
+ <uses-permission android:name="android.car.permission.CAR_CAMERA" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <application android:label="@string/app_title"
+ android:icon="@drawable/ic_launcher">
+
+ <!-- This is for embedded mode. -->
+ <activity android:name=".KitchenSinkProxyActivity"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:label="@string/app_title">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <meta-data
+ android:name="android.car.application"
+ android:resource="@xml/automotive_app_desc" />
+
+ <service android:name=".job.DishService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ </application>
+</manifest>
diff --git a/tests/EmbeddedKitchenSinkApp/res/drawable-hdpi/ic_launcher.png b/tests/EmbeddedKitchenSinkApp/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..af3d210
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-nodpi/sample_apps_bg.png b/tests/EmbeddedKitchenSinkApp/res/drawable-nodpi/app_bg.png
similarity index 100%
rename from tests/car_activity_test_app/res/drawable-nodpi/sample_apps_bg.png
rename to tests/EmbeddedKitchenSinkApp/res/drawable-nodpi/app_bg.png
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml b/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml
new file mode 100644
index 0000000..445a14f
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/audio.xml
@@ -0,0 +1,217 @@
+<?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: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="horizontal"
+ android:layout_weight="1" >
+ <ToggleButton
+ android:id="@+id/button_mock_audio"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/mock_audio_on"
+ android:textOff="@string/mock_audio_off" />
+ <ToggleButton
+ android:id="@+id/button_reject_audio_focus"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/reject_audio_focus_on"
+ android:textOff="@string/reject_audio_focus_off" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="1" >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <TextView
+ android:id="@+id/audio_focus_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_focus" />
+ <Button
+ android:id="@+id/button_audio_focus_request"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/request" />
+ <TextView
+ android:id="@+id/text_audio_focus_state"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/empty" />
+ </LinearLayout>
+ <RadioGroup
+ android:id="@+id/button_focus_request_selection"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <RadioButton
+ android:id="@+id/focus_gain"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/focus_gain" />
+ <RadioButton
+ android:id="@+id/focus_gain_transient_duck"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/focus_gain_transient_duck" />
+ <RadioButton
+ android:id="@+id/focus_release"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/focus_release" />
+ </RadioGroup>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_weight="1" >
+ <TextView
+ android:id="@+id/nav_play_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/nav_play" />
+ <Button
+ android:id="@+id/button_nav_play_once"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/play_pcm_once" />
+ <TextView
+ android:id="@+id/vr_play_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/vr_play" />
+ <Button
+ android:id="@+id/button_vr_play_once"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/play_pcm_once" />
+ <TextView
+ android:id="@+id/system_play_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/system_play" />
+ <Button
+ android:id="@+id/button_system_play_once"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/play_pcm_once" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_weight="1" >
+ <TextView
+ android:id="@+id/media_play_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/media_play" />
+ <Button
+ android:id="@+id/button_media_play_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/play" />
+ <Button
+ android:id="@+id/button_media_play_once"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/play_pcm_once" />
+ <Button
+ android:id="@+id/button_media_play_stop"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/stop" />
+ <Button
+ android:id="@+id/button_speaker_phone_on"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/speaker_phone_on" />
+ <Button
+ android:id="@+id/button_speaker_phone_off"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/speaker_phone_off" />
+ <Button
+ android:id="@+id/button_microphone_on"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/microphone_on" />
+ <Button
+ android:id="@+id/button_microphone_off"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/microphone_off" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/system_play_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/empty"
+ android:layout_weight="1" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_weight="1" >
+ <Button
+ android:id="@+id/button_nav_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/nav_start" />
+ <Button
+ android:id="@+id/button_nav_end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/nav_end" />
+ <Button
+ android:id="@+id/button_vr_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/vr_start" />
+ <Button
+ android:id="@+id/button_vr_end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/vr_end" />
+ <Button
+ android:id="@+id/button_radio_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_start" />
+ <Button
+ android:id="@+id/button_radio_end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_end" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/camera_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/camera_test.xml
new file mode 100644
index 0000000..ed167a5
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/camera_test.xml
@@ -0,0 +1,148 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:gravity="center"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:layout_marginTop="100dp"
+ android:orientation="vertical">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btnGetCap"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_getCapabilities"
+ android:textSize="@dimen/rvcTextSize"/>
+ <TextView
+ android:gravity="center"
+ android:id="@+id/tvCap"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:textSize="@dimen/rvcTextSize"/>
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btnGetRvcCrop"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_getRvcCrop"
+ android:textSize="@dimen/rvcTextSize"/>
+ <TextView
+ android:gravity="center"
+ android:id="@+id/tvRvcCrop"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:textSize="@dimen/rvcTextSize"/>
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btnGetRvcPos"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_getRvcPos"
+ android:textSize="@dimen/rvcTextSize"/>
+ <TextView
+ android:gravity="center"
+ android:id="@+id/tvRvcPos"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:textSize="@dimen/rvcTextSize"/>
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btnGetCameraState"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_getCameraState"
+ android:textSize="@dimen/rvcTextSize"/>
+ <TextView
+ android:gravity="center"
+ android:id="@+id/tvCameraState"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:textSize="@dimen/rvcTextSize"/>
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btnSetRvcCrop"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_setRvcCrop"
+ android:textSize="@dimen/rvcTextSize"/>
+ <Button
+ android:id="@+id/btnSetRvcCrop2"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_setRvcCrop"
+ android:textSize="@dimen/rvcTextSize"/>
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btnSetRvcPos"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_setRvcPos"
+ android:textSize="@dimen/rvcTextSize"/>
+ <Button
+ android:id="@+id/btnSetRvcPos2"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_setRvcPos"
+ android:textSize="@dimen/rvcTextSize"/>
+ <Button
+ android:id="@+id/btnSetRvcPos3"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_setRvcPos"
+ android:textSize="@dimen/rvcTextSize"/>
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+ <ToggleButton
+ android:id="@+id/btnRvcState"
+ android:layout_height="@dimen/rvcBtnHeight"
+ android:layout_width ="@dimen/rvcBtnWidth"
+ android:text="@string/rvc_state"
+ android:textSize="@dimen/rvcTextSize"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml
new file mode 100644
index 0000000..6cfe237
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml
@@ -0,0 +1,180 @@
+<?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:gravity="center"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_width = "wrap_content">
+
+ <Button
+ android:clickable="false"
+ android:id="@+id/btnDTempUp"
+ android:layout_height="100dp"
+ android:layout_width ="250dp"
+ android:text="@string/hvac_tempUp"
+ android:textSize="32sp"/>
+
+ <TextView
+ android:gravity="center"
+ android:id="@+id/tvDTemp"
+ android:layout_height="50dp"
+ android:layout_width ="250dp"
+ android:textSize="32sp"/>
+
+ <Button
+ android:clickable="false"
+ android:id="@+id/btnDTempDn"
+ android:layout_height="100dp"
+ android:layout_width ="250dp"
+ android:text="@string/hvac_tempDn"
+ android:textSize="32sp"/>
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:gravity="center"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_width = "wrap_content"
+ android:orientation="vertical">
+
+ <Button
+ android:clickable="false"
+ android:id="@+id/btnFanSpeedUp"
+ android:layout_height="100dp"
+ android:layout_width ="250dp"
+ android:text="@string/hvac_fanSpeedUp"
+ android:textSize="32sp"/>
+
+ <TextView
+ android:gravity="center"
+ android:id="@+id/tvFanSpeed"
+ android:layout_height="50dp"
+ android:layout_width ="250dp"
+ android:textSize="32sp"/>
+
+ <Button
+ android:clickable="false"
+ android:id="@+id/btnFanSpeedDn"
+ android:layout_height="100dp"
+ android:layout_width ="250dp"
+ android:text="@string/hvac_fanSpeedDn"
+ android:textSize="32sp"/>
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:gravity="center"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_width = "wrap_content"
+ android:orientation="vertical">
+
+ <Button
+ android:clickable="false"
+ android:id="@+id/btnPTempUp"
+ android:layout_height="100dp"
+ android:layout_width ="250dp"
+ android:text="@string/hvac_tempUp"
+ android:textSize="32sp"/>
+
+ <TextView
+ android:gravity="center"
+ android:id="@+id/tvPTemp"
+ android:layout_height="50dp"
+ android:layout_width ="250dp"
+ android:textSize="32sp"/>
+
+ <Button
+ android:clickable="false"
+ android:id="@+id/btnPTempDn"
+ android:layout_height="100dp"
+ android:layout_width ="250dp"
+ android:text="@string/hvac_tempDn"
+ android:textSize="32sp"/>
+ </LinearLayout>
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginTop="25dp"
+ android:orientation="horizontal">
+
+ <ToggleButton
+ android:clickable="false"
+ android:id="@+id/tbDefrostFront"
+ android:layout_height="100dp"
+ android:layout_weight="1"
+ android:layout_width ="150dp"
+ android:textOff="@string/hvac_defrostFrontOff"
+ android:textOn="@string/hvac_defrostFrontOn"
+ android:textSize="32sp"/>
+
+ <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
+ android:clickable="false"
+ android:id="@+id/rgFanPosition"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:orientation="vertical">
+ <RadioButton
+ android:clickable="false"
+ android:id="@+id/rbPositionFace"
+ android:text="@string/hvac_positionFace"
+ android:textSize="32sp"/>
+ <RadioButton
+ android:clickable="false"
+ android:id="@+id/rbPositionFloor"
+ android:text="@string/hvac_positionFloor"
+ android:textSize="32sp"/>
+ <RadioButton
+ android:clickable="false"
+ android:id="@+id/rbPositionFaceAndFloor"
+ android:text="@string/hvac_positionFaceAndFloor"
+ android:textSize="32sp"/>
+ </RadioGroup>
+
+ <ToggleButton
+ android:clickable="false"
+ android:id="@+id/tbAc"
+ android:layout_height="100dp"
+ android:layout_weight="1"
+ android:layout_width="150dp"
+ android:textOff="@string/hvac_acOff"
+ android:textOn="@string/hvac_acOn"
+ android:textSize="32sp"/>
+
+ <ToggleButton
+ android:clickable="false"
+ android:id="@+id/tbDefrostRear"
+ android:layout_height="100dp"
+ android:layout_weight="1"
+ android:layout_width ="150dp"
+ android:textOff="@string/hvac_defrostRearOff"
+ android:textOn="@string/hvac_defrostRearOn"
+ android:textSize="32sp"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml
new file mode 100644
index 0000000..f347ce9
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+<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="horizontal"
+ android:layout_weight="1" >
+ <Button
+ android:id="@+id/button_volume_up"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/volume_up" />
+ <Button
+ android:id="@+id/button_volume_down"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/volume_down" />
+ <Button
+ android:id="@+id/button_voice"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/voice" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
new file mode 100644
index 0000000..aebb27d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="40dp"
+ android:layout_marginLeft="40dp">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@string/cluster_start" android:id="@+id/cluster_start_button"
+ android:layout_column="0" android:textSize="32sp"/>
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@string/cluster_turn_left" android:id="@+id/cluster_turn_left_button"
+ android:layout_column="0" android:textSize="32sp"/>
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@string/cluster_stop" android:id="@+id/cluster_stop_button"
+ android:layout_column="0" android:textSize="32sp"/>
+ </LinearLayout>
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent"
+ android:inputType="textMultiLine"
+ android:ems="10"
+ android:id="@+id/cluster_log"
+ android:scrollIndicators="right" android:gravity="top"
+ android:editable="false"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/job_scheduler.xml b/tests/EmbeddedKitchenSinkApp/res/layout/job_scheduler.xml
new file mode 100644
index 0000000..f291f04
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/job_scheduler.xml
@@ -0,0 +1,119 @@
+<?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:orientation="horizontal" android:layout_width="match_parent"
+ android:layout_height="match_parent" android:layout_marginTop="90dp"
+ android:layout_marginLeft="90dp">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/refresh_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/refresh_button" />
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/cancel_button" />
+ </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/job_info"
+ android:textColor="@android:color/holo_red_dark" />
+ <TextView
+ android:id="@+id/current_jobs"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/schedule_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/schedule_button"/>
+ <CheckBox
+ android:id="@+id/require_idle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/require_idle"/>
+ <CheckBox
+ android:id="@+id/require_charging"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/require_charging"/>
+ <CheckBox
+ android:id="@+id/require_persisted"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/require_persisted"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/require_network"/>
+ <RadioGroup
+ android:id="@+id/network_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <RadioButton android:id="@+id/network_any"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/network_any"/>
+
+ <RadioButton android:id="@+id/network_none"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/network_none"/>
+
+ <RadioButton android:id="@+id/network_unmetered"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/network_unmetered"/>
+ </RadioGroup>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/dish_num"/>
+
+ <android.support.car.input.CarRestrictedEditText
+ android:id="@+id/dish_num"
+ android:layout_width="100dp"
+ android:layout_height="40dp"
+ android:inputType="number"
+ android:text="@string/default_dish_num" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_end_view.xml b/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_end_view.xml
new file mode 100644
index 0000000..d60f63d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_end_view.xml
@@ -0,0 +1,22 @@
+<?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:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ProgressBar
+ android:layout_width="50dp"
+ android:layout_height="50dp" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_test.xml
new file mode 100644
index 0000000..c617094
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/keyboard_test.xml
@@ -0,0 +1,53 @@
+<?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:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="96dp"
+ android:layout_marginLeft="96dp">
+
+ <TextView
+ android:id="@+id/search_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/on_search"
+ android:textSize="20dp" />
+
+ <TextView
+ android:id="@+id/edit_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/on_edit"
+ android:textSize="20dp" />
+
+ <Button
+ android:id="@+id/ime_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/open_ime_button"/>
+
+ <Button
+ android:id="@+id/stop_ime_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/close_ime_button"/>
+
+ <Button
+ android:id="@+id/ime_button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/open_kb_button"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_sink_activity.xml b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_sink_activity.xml
new file mode 100644
index 0000000..2801daa
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_sink_activity.xml
@@ -0,0 +1,22 @@
+<?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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="100dp"
+ android:orientation="vertical" >
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
new file mode 100644
index 0000000..25e6be6
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/one2six.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/one2six.mp3
new file mode 100644
index 0000000..1e2dc01
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/one2six.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/ring_classic_01.ogg b/tests/EmbeddedKitchenSinkApp/res/raw/ring_classic_01.ogg
new file mode 100644
index 0000000..b5e6077
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/ring_classic_01.ogg
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/turnright.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/turnright.mp3
new file mode 100644
index 0000000..6ef127e
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/turnright.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
new file mode 100644
index 0000000..e35ee3d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?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="rvcBtnHeight">40dp</dimen>
+ <dimen name="rvcBtnWidth">150dp</dimen>
+ <dimen name="rvcTextSize">10dp</dimen>
+ <dimen name="rvcTvHeight">80dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
new file mode 100644
index 0000000..9eeeaa9
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -0,0 +1,109 @@
+<?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_title">Kitchen Sink</string>
+ <!-- hvac -->
+ <string name="hvac_acOff">AC OFF</string>
+ <string name="hvac_acOn">AC ON</string>
+ <string name="hvac_defrostFrontOff">Front Defrost OFF</string>
+ <string name="hvac_defrostFrontOn">Front Defrost ON</string>
+ <string name="hvac_defrostRearOff">Rear Defrost OFF</string>
+ <string name="hvac_defrostRearOn">Rear Defrost ON</string>
+ <string name="hvac_positionFace">Face</string>
+ <string name="hvac_positionFloor">Floor</string>
+ <string name="hvac_positionFaceAndFloor">Face and Floor</string>
+ <string name="hvac_directionFaceAndDefrost">Face and Defrost</string>
+ <string name="hvac_fanSpeedDn">Fan Speed Down</string>
+ <string name="hvac_fanSpeedUp">Fan Speed Up</string>
+ <string name="hvac_tempDn">Temp Down</string>
+ <string name="hvac_tempUp">Temp Up</string>
+ <!-- camera -->
+ <string name="rvc_getCapabilities">Get Capabilities</string>
+ <string name="rvc_getRvcCrop">Get RVC Crop</string>
+ <string name="rvc_setRvcCrop">Set RVC Crop</string>
+ <string name="rvc_getRvcPos">Get RVC Pos</string>
+ <string name="rvc_setRvcPos">Set RVC Pos</string>
+ <string name="rvc_getCameraState">Get State</string>
+ <string name="rvc_setCameraState">Set State</string>
+ <string name="rvc_state">RVC State</string>
+ <!-- audio -->
+ <string name="empty"></string>
+ <string name="play">Play</string>
+ <string name="play_pcm_once">Play Once</string>
+ <string name="stop">Stop</string>
+ <string name="audio_focus">Audio Focus</string>
+ <string name="focus_gain">Gain</string>
+ <string name="focus_gain_transient_duck">Gain,Transient,Duck</string>
+ <string name="focus_release">Release</string>
+ <string name="request">request</string>
+ <string name="release">release</string>
+ <string name="media_play">Media Play</string>
+ <string name="nav_play">Nav Play</string>
+ <string name="vr_play">VR Play</string>
+ <string name="system_play">System Play</string>
+ <string name="config_index">Config Index</string>
+ <string name="choose_config">Choose config</string>
+ <string name="audio_stress_title">Audio Stress Test</string>
+ <string name="nav_start">Nav Start</string>
+ <string name="nav_end">Nav End</string>
+ <string name="vr_start">VR Start</string>
+ <string name="vr_end">VR End</string>
+ <string name="radio_start">Radio Start</string>
+ <string name="radio_end">Radio End</string>
+ <string name="speaker_phone_on">Speaker Phone On</string>
+ <string name="speaker_phone_off">Speaker Phone Off</string>
+ <string name="microphone_on">Mic On</string>
+ <string name="microphone_off">Mic Off</string>
+ <string name="mock_audio_on">Audio Mocking On</string>
+ <string name="mock_audio_off">Audio Mocking Off</string>
+ <string name="reject_audio_focus_on">Reject Audio Focus</string>
+ <string name="reject_audio_focus_off">Allow Audio Focus</string>
+
+ <!-- job scheduler -->
+ <string name="schedule_button">Schedule a New Job</string>
+ <string name="refresh_button">Refresh pending jobs</string>
+ <string name="cancel_button">Cancel All Jobs</string>
+ <string name="job_info">Current Pending Jobs</string>
+ <string name="require_charging">Require Charging</string>
+ <string name="require_persisted">Persisted</string>
+ <string name="require_periodic">Periodic</string>
+ <string name="require_network">Network Type</string>
+ <string name="require_idle">Device Idle</string>
+ <string name="dish_num">Number of Plates</string>
+ <string name="default_dish_num">50</string>
+ <string name="network_any">ANY</string>
+ <string name="network_none">NONE</string>
+ <string name="network_unmetered">UNMETERED</string>
+
+ <!-- keyboard test fragment -->
+ <string name="keyboard_test_title">Keyboard Test</string>
+ <string name="on_search">OnSearch:</string>
+ <string name="on_edit">onEdit:</string>
+ <string name="open_ime_button">Open IME</string>
+ <string name="close_ime_button">Close IME</string>
+ <string name="open_kb_button">Hide/Show Input</string>
+
+ <!-- instrument cluster -->
+ <string name="cluster_start">Start Nav</string>
+ <string name="cluster_turn_left">Turn left</string>
+ <string name="cluster_stop">Stop Nav</string>
+ <string name="cluster_nav_app_context_loss">Navigation app context lost!</string>
+
+ <!-- input test -->
+ <string name="volume_up">Volume +</string>
+ <string name="volume_down">Volume -</string>
+ <string name="voice">Voice</string>
+</resources>
diff --git a/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml b/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..9bb6ae7
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<automotiveApp>
+ <uses name="service" />
+ <uses name="projection" />
+ <uses name="activity" class="com.google.android.car.kitchensink.KitchenSinkProxyActivity" />
+</automotiveApp>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
new file mode 100644
index 0000000..29aad2c
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink;
+
+import android.car.Car;
+import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
+import android.car.test.VehicleHalEmulator;
+import android.os.SystemClock;
+
+import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioExtFocusFlag;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusIndex;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusRequest;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusState;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStream;
+import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
+import com.android.car.vehiclenetwork.VehiclePropValueUtil;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePermissionModel;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
+
+public class CarEmulator {
+
+ private final Car mCar;
+ private final VehicleHalEmulator mHalEmulator;
+
+ private final AudioFocusPropertyHandler mAudioFocusPropertyHandler =
+ new AudioFocusPropertyHandler();
+ private final AudioStreamStatePropertyHandler mAudioStreamStatePropertyHandler =
+ new AudioStreamStatePropertyHandler();
+
+ public CarEmulator(Car car) {
+ mCar = car;
+ mHalEmulator = new VehicleHalEmulator(car);
+ mHalEmulator.addProperty(
+ VehiclePropConfigUtil.getBuilder(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS,
+ VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE,
+ VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
+ VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC3,
+ VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
+ 0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
+ mAudioFocusPropertyHandler);
+ mHalEmulator.addProperty(
+ VehiclePropConfigUtil.getBuilder(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE,
+ VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE,
+ VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
+ VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2,
+ VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
+ 0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
+ mAudioStreamStatePropertyHandler);
+ }
+
+ public void start() {
+ mHalEmulator.start();
+ }
+
+ public void stop() {
+ mHalEmulator.stop();
+ }
+
+ public void setAudioFocusControl(boolean reject) {
+ mAudioFocusPropertyHandler.setAudioFocusControl(reject);
+ }
+
+ private class AudioFocusPropertyHandler implements VehicleHalPropertyHandler {
+ private boolean mRejectFocus;
+ private int mCurrentFocusState = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+ private int mCurrentFocusStreams = 0;
+ private int mCurrentFocusExtState = 0;
+
+ public void setAudioFocusControl(boolean reject) {
+ VehiclePropValue injectValue = null;
+ synchronized (this) {
+ if (reject) {
+ if (!mRejectFocus) {
+ mCurrentFocusState =
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE;
+ mCurrentFocusStreams = 0;
+ mCurrentFocusExtState = 0;
+ mRejectFocus = true;
+ int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+ mCurrentFocusExtState };
+ injectValue = VehiclePropValueUtil.createIntVectorValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+ SystemClock.elapsedRealtimeNanos());
+ }
+ } else {
+ if (mRejectFocus) {
+ mCurrentFocusState = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+ mCurrentFocusStreams = 0;
+ mCurrentFocusExtState = 0;
+ int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+ mCurrentFocusExtState };
+ injectValue = VehiclePropValueUtil.createIntVectorValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+ SystemClock.elapsedRealtimeNanos());
+ }
+ mRejectFocus = false;
+ }
+ }
+ if (injectValue != null) {
+ mHalEmulator.injectEvent(injectValue);
+ }
+ }
+
+ @Override
+ public void onPropertySet(VehiclePropValue value) {
+ VehiclePropValue injectValue = null;
+ synchronized (this) {
+ if (mRejectFocus) {
+ int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+ mCurrentFocusExtState };
+ injectValue = VehiclePropValueUtil.createIntVectorValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+ SystemClock.elapsedRealtimeNanos());
+ } else {
+ int request = value.getInt32Values(
+ VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_FOCUS);
+ int requestedStreams = value.getInt32Values(
+ VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_STREAMS);
+ int requestedExtFocus = value.getInt32Values(
+ VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE);
+ int response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+ switch (request) {
+ case VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN:
+ response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN;
+ break;
+ case VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT:
+ case VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK:
+ response =
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT;
+ break;
+ case VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE:
+ response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+ break;
+ }
+ mCurrentFocusState = response;
+ mCurrentFocusStreams = requestedStreams;
+ mCurrentFocusExtState = requestedExtFocus;
+ int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+ mCurrentFocusExtState };
+ injectValue = VehiclePropValueUtil.createIntVectorValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+ SystemClock.elapsedRealtimeNanos());
+ }
+ }
+ if (injectValue != null) {
+ mHalEmulator.injectEvent(injectValue);
+ }
+ }
+
+ @Override
+ public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ int[] values = { mCurrentFocusState, mCurrentFocusStreams,
+ mCurrentFocusExtState };
+ return VehiclePropValueUtil.createIntVectorValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+ SystemClock.elapsedRealtimeNanos());
+ }
+
+ @Override
+ public void onPropertySubscribe(int property, float sampleRate, int zones) {
+ }
+
+ @Override
+ public void onPropertyUnsubscribe(int property) {
+ }
+ }
+
+ private class AudioStreamStatePropertyHandler implements VehicleHalPropertyHandler {
+ @Override
+ public void onPropertySet(VehiclePropValue value) {
+ }
+
+ @Override
+ public VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ //ignore
+ return null;
+ }
+
+ @Override
+ public void onPropertySubscribe(int property, float sampleRate, int zones) {
+ }
+
+ @Override
+ public void onPropertyUnsubscribe(int property) {
+ }
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
new file mode 100644
index 0000000..60aafe8
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink;
+
+import com.google.android.car.kitchensink.audio.AudioTestFragment;
+import com.google.android.car.kitchensink.camera.CameraTestFragment;
+import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment;
+import com.google.android.car.kitchensink.hvac.HvacTestFragment;
+import com.google.android.car.kitchensink.input.InputTestFragment;
+import com.google.android.car.kitchensink.job.JobSchedulerFragment;
+import com.google.android.car.kitchensink.keyboard.KeyboardFragment;
+
+import android.car.hardware.camera.CarCameraManager;
+import android.car.hardware.hvac.CarHvacManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.car.Car;
+import android.support.car.CarAppContextManager;
+import android.support.car.CarNotConnectedException;
+import android.support.car.ServiceConnectionListener;
+import android.support.car.app.menu.CarDrawerActivity;
+import android.support.car.app.menu.CarMenu;
+import android.support.car.app.menu.CarMenuCallbacks;
+import android.support.car.app.menu.RootMenu;
+import android.support.car.hardware.CarSensorEvent;
+import android.support.car.hardware.CarSensorManager;
+import android.support.car.navigation.CarNavigationManager;
+import android.util.Log;
+
+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_CAMERA = "camera";
+ 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_KEYBOARD = "keyboard";
+ private static final String MENU_CLUSTER = "inst cluster";
+ private static final String MENU_INPUT_TEST = "input test";
+
+ private Car mCarApi;
+ private CarCameraManager mCameraManager;
+ private CarHvacManager mHvacManager;
+ private CarSensorManager mCarSensorManager;
+ private CarNavigationManager mCarNavigationManager;
+ private CarAppContextManager mCarAppContextManager;
+
+
+ private AudioTestFragment mAudioTestFragment;
+ private CameraTestFragment mCameraTestFragment;
+ private HvacTestFragment mHvacTestFragment;
+ private JobSchedulerFragment mJobFragment;
+ private KeyboardFragment mKeyboardFragment;
+ private InstrumentClusterFragment mInstrumentClusterFragment;
+ private InputTestFragment mInputTestFragment;
+
+ private final CarSensorManager.CarSensorEventListener mListener =
+ new CarSensorManager.CarSensorEventListener() {
+ @Override
+ public void onSensorChanged(CarSensorEvent event) {
+ switch (event.sensorType) {
+ case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
+ Log.d(TAG, "driving status:" + event.intValues[0]);
+ break;
+ }
+ }
+ };
+
+ public KitchenSinkActivity(Proxy proxy, Context context, Car car) {
+ super(proxy, context, car);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ resetTitle();
+ setScrimColor(Color.LTGRAY);
+ setLightMode();
+ setCarMenuCallbacks(new MyCarMenuCallbacks());
+ setContentView(R.layout.kitchen_sink_activity);
+
+ // Connection to Car Service does not work for non-automotive yet.
+ if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ mCarApi = Car.createCar(getContext(), mServiceConnectionListener);
+ mCarApi.connect();
+ }
+ Log.i(TAG, "onCreate");
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.i(TAG, "onStart");
+ }
+
+ @Override
+ protected void onRestart() {
+ super.onRestart();
+ Log.i(TAG, "onRestart");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.i(TAG, "onResume");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.i(TAG, "onPause");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.i(TAG, "onStop");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mCarSensorManager != null) {
+ mCarSensorManager.unregisterListener(mListener);
+ }
+ if (mCarApi != null) {
+ mCarApi.disconnect();
+ }
+ Log.i(TAG, "onDestroy");
+ }
+
+ private void resetTitle() {
+ setTitle(getContext().getString(R.string.app_title));
+ }
+
+ private final ServiceConnectionListener mServiceConnectionListener =
+ new ServiceConnectionListener() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(TAG, "Connected to Car Service");
+ try {
+ mCameraManager = (CarCameraManager) mCarApi.getCarManager(android.car.Car
+ .CAMERA_SERVICE);
+ mHvacManager = (CarHvacManager) mCarApi.getCarManager(android.car.Car.HVAC_SERVICE);
+ mCarNavigationManager = (CarNavigationManager) mCarApi.getCarManager(
+ android.car.Car.CAR_NAVIGATION_SERVICE);
+ mCarSensorManager = (CarSensorManager) mCarApi.getCarManager(Car.SENSOR_SERVICE);
+ mCarSensorManager.registerListener(mListener,
+ CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
+ CarSensorManager.SENSOR_RATE_NORMAL);
+ mCarAppContextManager =
+ (CarAppContextManager) mCarApi.getCarManager(Car.APP_CONTEXT_SERVICE);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!");
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "Disconnect from Car Service");
+ }
+
+ @Override
+ public void onServiceSuspended(int cause) {
+ Log.d(TAG, "Car Service connection suspended");
+ }
+
+ @Override
+ public void onServiceConnectionFailed(int cause) {
+ Log.d(TAG, "Car Service connection failed");
+ }
+ };
+
+ private final class MyCarMenuCallbacks extends CarMenuCallbacks {
+ /** Id for the root menu */
+ private static final String ROOT = "ROOT";
+
+ @Override
+ public RootMenu onGetRoot(Bundle hints) {
+ return new RootMenu(ROOT);
+ }
+
+ @Override
+ public void onLoadChildren(String parentId, CarMenu result) {
+ List<CarMenu.Item> items = new ArrayList<>();
+ if (parentId.equals(ROOT)) {
+ String[] allMenus = {
+ MENU_AUDIO, MENU_CAMERA, MENU_HVAC, MENU_JOB, MENU_KEYBOARD, MENU_CLUSTER,
+ MENU_INPUT_TEST, MENU_QUIT
+ };
+ for (String menu : allMenus) {
+ items.add(new CarMenu.Builder(menu).setText(menu).build());
+ }
+ }
+ result.sendResult(items);
+ }
+
+ @Override
+ public void onItemClicked(String id) {
+ Log.d(TAG, "onItemClicked id=" + id);
+ if (id.equals(MENU_AUDIO)) {
+ if (mAudioTestFragment == null) {
+ mAudioTestFragment = new AudioTestFragment();
+ }
+ setContentFragment(mAudioTestFragment);
+ } else if (id.equals(MENU_CAMERA)) {
+ if (mCameraManager != null) {
+ if (mCameraTestFragment == null) {
+ mCameraTestFragment = new CameraTestFragment();
+ mCameraTestFragment.setCameraManager(mCameraManager);
+ }
+ // Don't allow camera fragment to start if we don't have a manager.
+ setContentFragment(mCameraTestFragment);
+ }
+ } else if (id.equals(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.
+ setContentFragment(mHvacTestFragment);
+ }
+ } else if (id.equals(MENU_JOB)) {
+ if (mJobFragment == null) {
+ mJobFragment = new JobSchedulerFragment();
+ }
+ setContentFragment(mJobFragment);
+ } else if (id.equals(MENU_KEYBOARD)) {
+ if (mKeyboardFragment == null) {
+ mKeyboardFragment = new KeyboardFragment();
+ }
+ setContentFragment(mKeyboardFragment);
+ } else if (id.equals(MENU_CLUSTER)) {
+ if (mInstrumentClusterFragment == null) {
+ mInstrumentClusterFragment = new InstrumentClusterFragment();
+ mInstrumentClusterFragment.setCarNavigationManager(mCarNavigationManager);
+ mInstrumentClusterFragment.setCarAppContextManager(mCarAppContextManager);
+ }
+ setContentFragment(mInstrumentClusterFragment);
+ } else if (id.equals(MENU_INPUT_TEST)) {
+ if (mInputTestFragment == null) {
+ mInputTestFragment = new InputTestFragment();
+ }
+ setContentFragment(mInputTestFragment);
+ } else if (id.equals(MENU_QUIT)) {
+ finish();
+ }
+ }
+
+ @Override
+ public void onCarMenuClosed() {
+ resetTitle();
+ }
+ }
+}
diff --git a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarProxyActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkProxyActivity.java
similarity index 77%
rename from tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarProxyActivity.java
rename to tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkProxyActivity.java
index ce5ed1a..40bebd9 100644
--- a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarProxyActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkProxyActivity.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.support.car.test.caractivitytest;
+package com.google.android.car.kitchensink;
import android.support.car.app.CarProxyActivity;
-public class HelloCarProxyActivity extends CarProxyActivity {
+public class KitchenSinkProxyActivity extends CarProxyActivity {
- public HelloCarProxyActivity() {
- super(HelloCarActivity.class);
+ public KitchenSinkProxyActivity() {
+ super(KitchenSinkActivity.class, true);
}
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
new file mode 100644
index 0000000..618367d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.audio;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.util.Log;
+
+import com.google.android.car.kitchensink.R;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Class for playing music.
+ *
+ * This is not thread safe and all public calls should be made from main thread.
+ *
+ * MP3 used is from http://freemusicarchive.org/music/John_Harrison_with_the_Wichita_State_University_Chamber_Players/The_Four_Seasons_Vivaldi/05_-_Vivaldi_Summer_mvt_2_Adagio_-_John_Harrison_violin
+ * from John Harrison with the Wichita State University Chamber Players
+ * Copyright under Create Commons license.
+ */
+public class AudioPlayer {
+
+ public interface PlayStateListener {
+ void onCompletion();
+ }
+
+ private static final String TAG = AudioPlayer.class.getSimpleName();
+
+ private final AudioManager.OnAudioFocusChangeListener mFocusListener =
+ new AudioManager.OnAudioFocusChangeListener() {
+
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ Log.i(TAG, "audio focus change " + focusChange);
+ if (mPlayer == null) {
+ return;
+ }
+ if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ mPlayer.setVolume(1.0f, 1.0f);
+ if (mRepeat && isPlaying()) {
+ doResume();
+ }
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+ if (isPlaying()) {
+ mPlayer.setVolume(0.5f, 0.5f);
+ }
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT && mRepeat) {
+ if (isPlaying()) {
+ doPause();
+ }
+ } else {
+ if (isPlaying()) {
+ doStop();
+ }
+ }
+ }
+ };
+
+ private AudioManager mAudioManager;
+ private MediaPlayer mPlayer;
+
+ private final Context mContext;
+ private final int mResourceId;
+ private final AudioAttributes mAttrib;
+
+ private final AtomicBoolean mPlaying = new AtomicBoolean(false);
+
+ private volatile boolean mHandleFocus;
+ private volatile boolean mRepeat;
+
+ private PlayStateListener mListener;
+
+ public AudioPlayer(Context context, int resourceId, AudioAttributes attrib) {
+ mContext = context;
+ mResourceId = resourceId;
+ mAttrib = attrib;
+ }
+
+ public void start(boolean handleFocus, boolean repeat, int focusRequest) {
+ mHandleFocus = handleFocus;
+ mRepeat = repeat;
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ int ret = AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ if (mHandleFocus) {
+ ret = mAudioManager.requestAudioFocus(mFocusListener, mAttrib,
+ focusRequest, 0);
+ }
+ if (ret == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ doStart();
+ } else {
+ Log.i(TAG, "no focus");
+ }
+ }
+
+ public void start(boolean handleFocus, boolean repeat, int focusRequest,
+ PlayStateListener listener) {
+ mListener = listener;
+ start(handleFocus, repeat, focusRequest);
+ }
+
+ private void doStart() {
+ if (mPlaying.getAndSet(true)) {
+ Log.i(TAG, "already playing");
+ return;
+ }
+ Log.i(TAG, "doStart audio");
+ mPlayer = new MediaPlayer();
+ mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log.e(TAG, "audio error what " + what + " extra " + extra);
+ mPlaying.set(false);
+ if (!mRepeat && mHandleFocus) {
+ mPlayer.stop();
+ mPlayer.release();
+ mPlayer = null;
+ mAudioManager.abandonAudioFocus(mFocusListener);
+ }
+ return false;
+ }
+
+ });
+ mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ mPlaying.set(false);
+ if (!mRepeat && mHandleFocus) {
+ mPlayer.stop();
+ mPlayer.release();
+ mPlayer = null;
+ mAudioManager.abandonAudioFocus(mFocusListener);
+ if (mListener != null) {
+ mListener.onCompletion();
+ }
+ }
+ }
+ });
+ mPlayer.setAudioAttributes(mAttrib);
+ mPlayer.setLooping(mRepeat);
+ mPlayer.setVolume(1.0f, 1.0f);
+ try {
+ AssetFileDescriptor afd =
+ mContext.getResources().openRawResourceFd(mResourceId);
+ if (afd == null) {
+ throw new RuntimeException("no res");
+ }
+ mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
+ afd.getLength());
+ afd.close();
+ mPlayer.prepare();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ mPlayer.start();
+ }
+
+ public void stop() {
+ doStop();
+ if (mHandleFocus) {
+ mAudioManager.abandonAudioFocus(mFocusListener);
+ }
+ }
+
+ public void release() {
+ if (isPlaying()) {
+ stop();
+ }
+ }
+
+ private void doStop() {
+ if (!mPlaying.getAndSet(false)) {
+ Log.i(TAG, "already stopped");
+ return;
+ }
+ Log.i(TAG, "doStop audio");
+ mPlayer.stop();
+ mPlayer.release();
+ mPlayer = null;
+ }
+
+ private void doPause() {
+ mPlayer.pause();
+ }
+
+ private void doResume() {
+ mPlayer.start();
+ }
+
+ public boolean isPlaying() {
+ return mPlaying.get();
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
new file mode 100644
index 0000000..5ac40ea
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.audio;
+
+import android.car.Car;
+import android.car.CarAppContextManager;
+import android.car.CarAppContextManager.AppContextChangeListener;
+import android.car.CarNotConnectedException;
+import android.car.media.CarAudioManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.google.android.car.kitchensink.CarEmulator;
+import com.google.android.car.kitchensink.R;
+import com.google.android.car.kitchensink.audio.AudioPlayer.PlayStateListener;
+
+public class AudioTestFragment extends Fragment {
+ private static final String TAG = "CAR.AUDIO.KS";
+ private static final boolean DBG = true;
+
+ private AudioManager mAudioManager;
+ private FocusHandler mAudioFocusHandler;
+ private Button mNavPlayOnce;
+ private Button mVrPlayOnce;
+ private Button mSystemPlayOnce;
+ private Button mMediaPlay;
+ private Button mMediaPlayOnce;
+ private Button mMediaStop;
+ private Button mNavStart;
+ private Button mNavEnd;
+ private Button mVrStart;
+ private Button mVrEnd;
+ private Button mRadioStart;
+ private Button mRadioEnd;
+ private Button mSpeakerPhoneOn;
+ private Button mSpeakerPhoneOff;
+ private Button mMicrophoneOn;
+ private Button mMicrophoneOff;
+ private ToggleButton mEnableMocking;
+ private ToggleButton mRejectFocus;
+
+ private AudioPlayer mMusicPlayer;
+ private AudioPlayer mMusicPlayerShort;
+ private AudioPlayer mNavGuidancePlayer;
+ private AudioPlayer mVrPlayer;
+ private AudioPlayer mSystemPlayer;
+ private AudioPlayer[] mAllPlayers;
+
+ private Handler mHandler;
+ private Context mContext;
+
+ private Car mCar;
+ private CarAppContextManager mAppContextManager;
+ private CarAudioManager mCarAudioManager;
+ private AudioAttributes mMusicAudioAttrib;
+ private AudioAttributes mNavAudioAttrib;
+ private AudioAttributes mVrAudioAttrib;
+ private AudioAttributes mRadioAudioAttrib;
+ private AudioAttributes mSystemSoundAudioAttrib;
+ private CarEmulator mCarEmulator;
+
+ private final AudioManager.OnAudioFocusChangeListener mNavFocusListener =
+ new AudioManager.OnAudioFocusChangeListener() {
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ Log.i(TAG, "Nav focus change:" + focusChange);
+ }
+ };
+ private final AudioManager.OnAudioFocusChangeListener mVrFocusListener =
+ new AudioManager.OnAudioFocusChangeListener() {
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ Log.i(TAG, "VR focus change:" + focusChange);
+ }
+ };
+ private final AudioManager.OnAudioFocusChangeListener mRadioFocusListener =
+ new AudioManager.OnAudioFocusChangeListener() {
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ Log.i(TAG, "Radio focus change:" + focusChange);
+ }
+ };
+
+ private void init() {
+ mContext = getContext();
+ mHandler = new Handler(Looper.getMainLooper());
+ mCar = Car.createCar(mContext, new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mAppContextManager =
+ (CarAppContextManager) mCar.getCarManager(Car.APP_CONTEXT_SERVICE);
+ mAppContextManager.registerContextListener(new AppContextChangeListener() {
+
+ @Override
+ public void onAppContextOwnershipLoss(int context) {
+ }
+
+ @Override
+ public void onAppContextChange(int activeContexts) {
+ }
+ }, CarAppContextManager.APP_CONTEXT_NAVIGATION |
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+ mMusicAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+ CarAudioManager.CAR_AUDIO_USAGE_MUSIC);
+ mNavAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+ CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE);
+ mVrAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+ CarAudioManager.CAR_AUDIO_USAGE_VOICE_COMMAND);
+ mRadioAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+ CarAudioManager.CAR_AUDIO_USAGE_RADIO);
+ mSystemSoundAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage(
+ CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND);
+ mMusicPlayer = new AudioPlayer(mContext, R.raw.john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio,
+ mMusicAudioAttrib);
+ mMusicPlayerShort = new AudioPlayer(mContext, R.raw.ring_classic_01,
+ mMusicAudioAttrib);
+ mNavGuidancePlayer = new AudioPlayer(mContext, R.raw.turnright,
+ mNavAudioAttrib);
+ // no Usage for voice command yet.
+ mVrPlayer = new AudioPlayer(mContext, R.raw.one2six,
+ mVrAudioAttrib);
+ mSystemPlayer = new AudioPlayer(mContext, R.raw.ring_classic_01,
+ mSystemSoundAudioAttrib);
+ mAllPlayers = new AudioPlayer[] {
+ mMusicPlayer,
+ mMusicPlayerShort,
+ mNavGuidancePlayer,
+ mVrPlayer,
+ mSystemPlayer
+ };
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ }, Looper.getMainLooper());
+ mCar.connect();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ Log.i(TAG, "onCreateView");
+ init();
+ View view = inflater.inflate(R.layout.audio, container, false);
+ mAudioManager = (AudioManager) mContext.getSystemService(
+ Context.AUDIO_SERVICE);
+ mAudioFocusHandler = new FocusHandler(
+ (RadioGroup) view.findViewById(R.id.button_focus_request_selection),
+ (Button) view.findViewById(R.id.button_audio_focus_request),
+ (TextView) view.findViewById(R.id.text_audio_focus_state));
+ mMediaPlay = (Button) view.findViewById(R.id.button_media_play_start);
+ mMediaPlay.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mMusicPlayer.start(false, true, AudioManager.AUDIOFOCUS_GAIN);
+ }
+ });
+ mMediaPlayOnce = (Button) view.findViewById(R.id.button_media_play_once);
+ mMediaPlayOnce.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mMusicPlayerShort.start(true, false, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+ // play only for 1 sec and stop
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mMusicPlayerShort.stop();
+ }
+ }, 1000);
+ }
+ });
+ mMediaStop = (Button) view.findViewById(R.id.button_media_play_stop);
+ mMediaStop.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mMusicPlayer.stop();
+ }
+ });
+ mNavPlayOnce = (Button) view.findViewById(R.id.button_nav_play_once);
+ mNavPlayOnce.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mAppContextManager == null) {
+ return;
+ }
+ if (DBG) {
+ Log.i(TAG, "Nav start");
+ }
+ if (!mNavGuidancePlayer.isPlaying()) {
+ mAppContextManager.setActiveContexts(
+ CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mNavGuidancePlayer.start(true, false,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ new PlayStateListener() {
+ @Override
+ public void onCompletion() {
+ mAppContextManager.resetActiveContexts(
+ CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ }
+ });
+ }
+ }
+ });
+ mVrPlayOnce = (Button) view.findViewById(R.id.button_vr_play_once);
+ mVrPlayOnce.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mAppContextManager == null) {
+ return;
+ }
+ if (DBG) {
+ Log.i(TAG, "VR start");
+ }
+ mAppContextManager.setActiveContexts(
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ if (!mVrPlayer.isPlaying()) {
+ mVrPlayer.start(true, false, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
+ new PlayStateListener() {
+ @Override
+ public void onCompletion() {
+ mAppContextManager.resetActiveContexts(
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ }
+ });
+ }
+ }
+ });
+ mSystemPlayOnce = (Button) view.findViewById(R.id.button_system_play_once);
+ mSystemPlayOnce.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (DBG) {
+ Log.i(TAG, "System start");
+ }
+ if (!mSystemPlayer.isPlaying()) {
+ // system sound played without focus
+ mSystemPlayer.start(false, false, 0);
+ }
+ }
+ });
+ mNavStart = (Button) view.findViewById(R.id.button_nav_start);
+ mNavStart.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleNavStart();
+ }
+ });
+ mNavEnd = (Button) view.findViewById(R.id.button_nav_end);
+ mNavEnd.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleNavEnd();
+ }
+ });
+ mVrStart = (Button) view.findViewById(R.id.button_vr_start);
+ mVrStart.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleVrStart();
+ }
+ });
+ mVrEnd = (Button) view.findViewById(R.id.button_vr_end);
+ mVrEnd.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleVrEnd();
+ }
+ });
+ mRadioStart = (Button) view.findViewById(R.id.button_radio_start);
+ mRadioStart.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleRadioStart();
+ }
+ });
+ mRadioEnd = (Button) view.findViewById(R.id.button_radio_end);
+ mRadioEnd.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleRadioEnd();
+ }
+ });
+ mSpeakerPhoneOn = (Button) view.findViewById(R.id.button_speaker_phone_on);
+ mSpeakerPhoneOn.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAudioManager.setSpeakerphoneOn(true);
+ }
+ });
+ mSpeakerPhoneOff = (Button) view.findViewById(R.id.button_speaker_phone_off);
+ mSpeakerPhoneOff.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAudioManager.setSpeakerphoneOn(false);
+ }
+ });
+ mMicrophoneOn = (Button) view.findViewById(R.id.button_microphone_on);
+ mMicrophoneOn.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAudioManager.setMicrophoneMute(false); // Turn the microphone on.
+ }
+ });
+ mMicrophoneOff = (Button) view.findViewById(R.id.button_microphone_off);
+ mMicrophoneOff.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAudioManager.setMicrophoneMute(true); // Mute the microphone.
+ }
+ });
+
+
+ mRejectFocus = (ToggleButton) view.findViewById(R.id.button_reject_audio_focus);
+ mRejectFocus.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (mCarEmulator == null) {
+ return;
+ }
+ if (!mEnableMocking.isChecked()) {
+ return;
+ }
+ if (isChecked) {
+ mCarEmulator.setAudioFocusControl(true);
+ } else {
+ mCarEmulator.setAudioFocusControl(false);
+ }
+ }
+ });
+ mRejectFocus.setActivated(false);
+ mEnableMocking = (ToggleButton) view.findViewById(R.id.button_mock_audio);
+ mEnableMocking.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (mCarEmulator == null) {
+ mCarEmulator = new CarEmulator(mCar);
+ }
+ if (isChecked) {
+ mRejectFocus.setActivated(true);
+ mCarEmulator.start();
+ } else {
+ mRejectFocus.setActivated(false);
+ mCarEmulator.stop();
+ mCarEmulator = null;
+ }
+ }
+ });
+ return view;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ Log.i(TAG, "onDestroyView");
+ if (mCarEmulator != null) {
+ mCarEmulator.setAudioFocusControl(false);
+ mCarEmulator.stop();
+ }
+ for (AudioPlayer p : mAllPlayers) {
+ p.stop();
+ }
+ if (mAudioFocusHandler != null) {
+ mAudioFocusHandler.release();
+ mAudioFocusHandler = null;
+ }
+ if (mAppContextManager != null) {
+ mAppContextManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION |
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ }
+ }
+
+ private void handleNavStart() {
+ if (mAppContextManager == null) {
+ return;
+ }
+ if (mCarAudioManager == null) {
+ return;
+ }
+ if (DBG) {
+ Log.i(TAG, "Nav start");
+ }
+ mAppContextManager.setActiveContexts(
+ CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mCarAudioManager.requestAudioFocus(mNavFocusListener, mNavAudioAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ }
+
+ private void handleNavEnd() {
+ if (mAppContextManager == null) {
+ return;
+ }
+ if (mCarAudioManager == null) {
+ return;
+ }
+ if (DBG) {
+ Log.i(TAG, "Nav end");
+ }
+ mAppContextManager.resetActiveContexts(
+ CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mCarAudioManager.abandonAudioFocus(mNavFocusListener, mNavAudioAttrib);
+ }
+
+ private void handleVrStart() {
+ if (mAppContextManager == null) {
+ return;
+ }
+ if (mCarAudioManager == null) {
+ return;
+ }
+ if (DBG) {
+ Log.i(TAG, "VR start");
+ }
+ mAppContextManager.setActiveContexts(
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ mCarAudioManager.requestAudioFocus(mVrFocusListener, mVrAudioAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 0);
+ }
+
+ private void handleVrEnd() {
+ if (mAppContextManager == null) {
+ return;
+ }
+ if (mCarAudioManager == null) {
+ return;
+ }
+ if (DBG) {
+ Log.i(TAG, "VR end");
+ }
+ mAppContextManager.resetActiveContexts(
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ mCarAudioManager.abandonAudioFocus(mVrFocusListener, mVrAudioAttrib);
+ }
+
+ private void handleRadioStart() {
+ if (mCarAudioManager == null) {
+ return;
+ }
+ if (DBG) {
+ Log.i(TAG, "Radio start");
+ }
+ mCarAudioManager.requestAudioFocus(mRadioFocusListener, mRadioAudioAttrib,
+ AudioManager.AUDIOFOCUS_GAIN, 0);
+ }
+
+ private void handleRadioEnd() {
+ if (mCarAudioManager == null) {
+ return;
+ }
+ if (DBG) {
+ Log.i(TAG, "Radio end");
+ }
+ mCarAudioManager.abandonAudioFocus(mRadioFocusListener, mRadioAudioAttrib);
+ }
+
+ private class FocusHandler {
+ private static final String AUDIO_FOCUS_STATE_GAIN = "gain";
+ private static final String AUDIO_FOCUS_STATE_RELEASED_UNKNOWN = "released / unknown";
+
+ private final RadioGroup mRequestSelection;
+ private final TextView mText;
+ private final AudioFocusListener mFocusListener;
+
+ public FocusHandler(RadioGroup radioGroup, Button requestButton, TextView text) {
+ mText = text;
+ mRequestSelection = radioGroup;
+ mRequestSelection.check(R.id.focus_gain);
+ setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN);
+ mFocusListener = new AudioFocusListener();
+ requestButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int selectedButtonId = mRequestSelection.getCheckedRadioButtonId();
+ int focusRequest = AudioManager.AUDIOFOCUS_GAIN;
+ if (selectedButtonId == R.id.focus_gain_transient_duck) {
+ focusRequest = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
+ } else if (selectedButtonId == R.id.focus_release) {
+ mAudioManager.abandonAudioFocus(mFocusListener);
+ setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN);
+ return;
+ }
+ int ret = mAudioManager.requestAudioFocus(mFocusListener,
+ AudioManager.STREAM_MUSIC, focusRequest);
+ Log.i(TAG, "requestAudioFocus returned " + ret);
+ if (ret == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ setFocusText(AUDIO_FOCUS_STATE_GAIN);
+ }
+ }
+ });
+ }
+
+ public void release() {
+ abandonAudioFocus();
+ }
+
+ private void abandonAudioFocus() {
+ if (DBG) {
+ Log.i(TAG, "abandonAudioFocus");
+ }
+ mAudioManager.abandonAudioFocus(mFocusListener);
+ setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN);
+ }
+
+ private void setFocusText(String msg) {
+ mText.setText("focus state:" + msg);
+ }
+
+ private class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ Log.i(TAG, "onAudioFocusChange " + focusChange);
+ if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ setFocusText(AUDIO_FOCUS_STATE_GAIN);
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+ setFocusText("loss");
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+ setFocusText("loss,transient");
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+ setFocusText("loss,transient,duck");
+ }
+ }
+ }
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/camera/CameraTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/camera/CameraTestFragment.java
new file mode 100644
index 0000000..8d68cf4
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/camera/CameraTestFragment.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.camera;
+
+import android.os.Bundle;
+import android.car.CarNotConnectedException;
+import android.car.hardware.camera.CarCamera;
+import android.car.hardware.camera.CarCameraState;
+import android.car.hardware.camera.CarCameraManager;
+import android.graphics.Rect;
+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.Button;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.google.android.car.kitchensink.R;
+
+import java.lang.Override;
+
+public class CameraTestFragment extends Fragment {
+ private final boolean DBG = true;
+ private final String TAG = "CameraTestFragment";
+ private TextView mTvCap;
+ private TextView mTvRvcCrop;
+ private TextView mTvRvcPos;
+ private TextView mTvCameraState;
+ private CarCameraManager mCarCameraManager;
+ private CarCamera mRvcCamera;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
+ View v = inflater.inflate(R.layout.camera_test, container, false);
+
+ int[] cameraList = mCarCameraManager.getCameraList();
+ for (int camera : cameraList) {
+ if (camera == CarCameraManager.CAR_CAMERA_TYPE_RVC) {
+ mRvcCamera = mCarCameraManager.openCamera(1);
+ break;
+ }
+ }
+
+ mTvCap = (TextView)v.findViewById(R.id.tvCap);
+ mTvRvcCrop = (TextView)v.findViewById(R.id.tvRvcCrop);
+ mTvRvcPos = (TextView)v.findViewById(R.id.tvRvcPos);
+ mTvCameraState = (TextView)v.findViewById(R.id.tvCameraState);
+
+ Button btn = (Button) v.findViewById(R.id.btnGetCap);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ int cap = mRvcCamera.getCapabilities();
+ mTvCap.setText(String.valueOf(cap));
+ }
+ });
+
+ btn = (Button) v.findViewById(R.id.btnGetRvcCrop);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Rect rect = mRvcCamera.getCameraCrop();
+ if(rect != null) {
+ mTvRvcCrop.setText(rect.toString());
+ } else {
+ mTvRvcCrop.setText("null");
+ }
+ }
+ });
+
+ btn = (Button) v.findViewById(R.id.btnGetRvcPos);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Rect rect = mRvcCamera.getCameraPosition();
+ if(rect != null) {
+ mTvRvcPos.setText(String.valueOf(rect));
+ } else {
+ mTvRvcPos.setText("null");
+ }
+ }
+ });
+
+ btn = (Button) v.findViewById(R.id.btnGetCameraState);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ CarCameraState state = mRvcCamera.getCameraState();
+ if(state != null) {
+ mTvCameraState.setText(state.toString());
+ } else {
+ mTvCameraState.setText("null");
+ }
+ }
+ });
+
+ btn = (Button) v.findViewById(R.id.btnSetRvcCrop);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Rect rect = new Rect(160, 240, 560, 480);
+ mRvcCamera.setCameraCrop(rect);
+ }
+ });
+
+ btn = (Button) v.findViewById(R.id.btnSetRvcCrop2);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Rect rect = new Rect(0, 0, 720, 480);
+ mRvcCamera.setCameraCrop(rect);
+ }
+ });
+
+ btn = (Button) v.findViewById(R.id.btnSetRvcPos);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Rect rect = new Rect(300, 0, 800, 480);
+ mRvcCamera.setCameraPosition(rect);
+ }
+ });
+
+ btn = (Button) v.findViewById(R.id.btnSetRvcPos2);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Rect rect = new Rect(500, 0, 800, 480);
+ mRvcCamera.setCameraPosition(rect);
+ }
+ });
+
+ btn = (Button) v.findViewById(R.id.btnSetRvcPos3);
+ btn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Rect rect = new Rect(300, 0, 500, 300);
+ mRvcCamera.setCameraPosition(rect);
+ }
+ });
+
+ final ToggleButton toggleBtn = (ToggleButton) v.findViewById(R.id.btnRvcState);
+ toggleBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ CarCameraState state = new CarCameraState(false, toggleBtn.isChecked());
+ mRvcCamera.setCameraState(state);
+ }
+ });
+
+ if(DBG) {
+ Log.d(TAG, "Starting CameraTestFragment");
+ }
+ return v;
+ }
+
+ public void setCameraManager(CarCameraManager cameraManager) {
+ Log.d(TAG, "setCameraManager()");
+ mCarCameraManager = cameraManager;
+ }
+}
+
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
new file mode 100644
index 0000000..f3bc327
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.cluster;
+
+import com.google.android.car.kitchensink.R;
+
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.car.CarAppContextManager;
+import android.support.car.CarAppContextManager.AppContextChangeListener;
+import android.support.car.CarNotConnectedException;
+import android.support.car.navigation.CarNavigationManager;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+
+/**
+ * Contains functions to test instrument cluster API.
+ */
+public class InstrumentClusterFragment extends Fragment {
+ private static final String TAG = InstrumentClusterFragment.class.getSimpleName();
+
+ private CarNavigationManager mCarNavigationManager;
+ private CarAppContextManager mCarAppContextManager;
+
+ public void setCarNavigationManager(CarNavigationManager carNavigationManager) {
+ mCarNavigationManager = carNavigationManager;
+ }
+
+ public void setCarAppContextManager(CarAppContextManager carAppContextManager) {
+ mCarAppContextManager = carAppContextManager;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.instrument_cluster, container);
+
+ view.findViewById(R.id.cluster_start_button).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ initCluster();
+ }
+ });
+ view.findViewById(R.id.cluster_turn_left_button).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ turnLeft();
+ }
+ });
+
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ private void turnLeft() {
+ try {
+ mCarNavigationManager.sendNavigationTurnEvent(CarNavigationManager.TURN_TURN,
+ "Huff Ave", 90, -1, null, CarNavigationManager.TURN_SIDE_LEFT);
+ mCarNavigationManager.sendNavigationTurnDistanceEvent(500, 10);
+ } catch (CarNotConnectedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void initCluster() {
+ mCarAppContextManager.registerContextListener(new AppContextChangeListener() {
+ @Override
+ public void onAppContextChange(int activeContexts) {
+ Log.d(TAG, "onAppContextChange, activeContexts: " + activeContexts);
+ }
+
+ @Override
+ public void onAppContextOwnershipLoss(int context) {
+ Log.w(TAG, "onAppContextOwnershipLoss, context: " + context);
+
+ new AlertDialog.Builder(getContext())
+ .setTitle(getContext().getApplicationInfo().name)
+ .setMessage(R.string.cluster_nav_app_context_loss)
+ .show();
+ }
+ }, CarAppContextManager.APP_CONTEXT_NAVIGATION);
+
+ mCarAppContextManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ boolean ownsContext =
+ mCarAppContextManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ Log.d(TAG, "Owns APP_CONTEXT_NAVIGATION: " + ownsContext);
+ if (!ownsContext) {
+ throw new RuntimeException("Context was not acquired.");
+ }
+
+ try {
+ mCarNavigationManager.sendNavigationStatus(CarNavigationManager.STATUS_ACTIVE);
+ } catch (CarNotConnectedException e) {
+ e.printStackTrace();
+ }
+ }
+}
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
new file mode 100644
index 0000000..c22cd6b
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.hvac;
+
+import android.car.CarNotConnectedException;
+import android.car.hardware.hvac.CarHvacEvent;
+import android.car.hardware.hvac.CarHvacManager.CarHvacBaseProperty;
+import android.car.hardware.hvac.CarHvacManager.CarHvacBooleanValue;
+import android.car.hardware.hvac.CarHvacManager.CarHvacFloatProperty;
+import android.car.hardware.hvac.CarHvacManager.CarHvacFloatValue;
+import android.car.hardware.hvac.CarHvacManager.CarHvacIntProperty;
+import android.car.hardware.hvac.CarHvacManager.CarHvacIntValue;
+import android.car.hardware.hvac.CarHvacManager;
+import android.os.Bundle;
+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.Button;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleHvacFanDirection;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleWindow;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleZone;
+
+import com.google.android.car.kitchensink.R;
+
+import java.lang.Override;
+import java.util.List;
+
+public class HvacTestFragment extends Fragment {
+ private final boolean DBG = true;
+ private final String TAG = "HvacTestFragment";
+ private RadioButton mRbFanPositionFace;
+ private RadioButton mRbFanPositionFloor;
+ private RadioButton mRbFanPositionFaceAndFloor;
+ private ToggleButton mTbAc;
+ private ToggleButton mTbDefrostFront;
+ private ToggleButton mTbDefrostRear;
+ private TextView mTvFanSpeed;
+ private TextView mTvDTemp;
+ private TextView mTvPTemp;
+ private int mCurFanSpeed = 1;
+ private float mCurDTemp = 23;
+ private float mCurPTemp = 23;
+ private CarHvacManager mCarHvacManager;
+
+ private final CarHvacManager.CarHvacEventListener mHvacListener =
+ new CarHvacManager.CarHvacEventListener () {
+ @Override
+ public void onChangeEvent(final CarHvacManager.CarHvacBaseProperty value) {
+ int zone = value.getZone();
+ switch(value.getPropertyId()) {
+ case CarHvacManager.HVAC_ZONED_AC_ON:
+ mTbAc.setChecked(((CarHvacBooleanValue)value).getValue());
+ break;
+ case CarHvacManager.HVAC_ZONED_FAN_POSITION:
+ switch(((CarHvacIntValue)value).getValue()) {
+ case VehicleHvacFanDirection.VEHICLE_HVAC_FAN_DIRECTION_FACE:
+ mRbFanPositionFace.setChecked(true);
+ break;
+ case VehicleHvacFanDirection.VEHICLE_HVAC_FAN_DIRECTION_FLOOR:
+ mRbFanPositionFloor.setChecked(true);
+ break;
+ case VehicleHvacFanDirection.
+ VEHICLE_HVAC_FAN_DIRECTION_FACE_AND_FLOOR:
+ mRbFanPositionFaceAndFloor.setChecked(true);
+ break;
+ default:
+ Log.e(TAG, "Unknown fan position: " +
+ ((CarHvacIntValue)value).getValue());
+ break;
+ }
+ break;
+ case CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT:
+ mCurFanSpeed = ((CarHvacIntValue)value).getValue();
+ mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+ break;
+ case CarHvacManager.HVAC_ZONED_TEMP_SETPOINT:
+ switch(zone) {
+ case VehicleZone.VEHICLE_ZONE_ROW_1_LEFT:
+ mCurDTemp = ((CarHvacFloatValue)value).getValue();
+ mTvDTemp.setText(String.valueOf(mCurDTemp));
+ break;
+ case VehicleZone.VEHICLE_ZONE_ROW_1_RIGHT:
+ mCurPTemp = ((CarHvacFloatValue)value).getValue();
+ mTvPTemp.setText(String.valueOf(mCurPTemp));
+ break;
+ default:
+ Log.w(TAG, "Unknown zone = " + zone);
+ break;
+ }
+ break;
+ case CarHvacManager.HVAC_WINDOW_DEFROSTER_ON:
+ if((zone & VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD) ==
+ VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD) {
+ mTbDefrostFront.setChecked(((CarHvacBooleanValue)value).getValue());
+ }
+ if((zone & VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD) ==
+ VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD) {
+ mTbDefrostRear.setChecked(((CarHvacBooleanValue)value).getValue());
+ }
+ break;
+ default:
+ Log.d(TAG, "onChangeEvent(): unknown property id = " + value
+ .getPropertyId());
+ }
+ }
+
+ @Override
+ public void onErrorEvent(final int propertyId, final int zone) {
+ Log.d(TAG, "Error: propertyId=" + propertyId + " zone=" + zone);
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ try {
+ mCarHvacManager.registerListener(mHvacListener);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!");
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mCarHvacManager.unregisterListener();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
+ View v = inflater.inflate(R.layout.hvac_test, container, false);
+
+ List<CarHvacBaseProperty> props = mCarHvacManager.getPropertyList();
+
+ for(CarHvacBaseProperty prop : props) {
+ int propId = prop.getPropertyId();
+ int type = prop.getType();
+
+ if(DBG) {
+ switch(type) {
+ case CarHvacManager.PROPERTY_TYPE_BOOLEAN:
+ Log.d(TAG, "propertyId = " + propId + " type = " + type +
+ " zone = " + prop.getZone());
+ break;
+ case CarHvacManager.PROPERTY_TYPE_FLOAT:
+ CarHvacFloatProperty f = (CarHvacFloatProperty)prop;
+ Log.d(TAG, "propertyId = " + propId + " type = " + type +
+ " zone = " + f.getZone() + " min = " + f.getMinValue() +
+ " max = " + f.getMaxValue());
+ break;
+ case CarHvacManager.PROPERTY_TYPE_INT:
+ CarHvacIntProperty i = (CarHvacIntProperty)prop;
+ Log.d(TAG, "propertyId = " + propId + " type = " + type +
+ " zone = " + i.getZone() + " min = " + i.getMinValue() +
+ " max = " + i.getMaxValue());
+ break;
+ }
+ }
+
+ switch(propId) {
+ case CarHvacManager.HVAC_ZONED_AC_ON:
+ configureAcOn(v);
+ break;
+ case CarHvacManager.HVAC_ZONED_FAN_POSITION:
+ configureFanPosition(v);
+ break;
+ case CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT:
+ configureFanSpeed(v);
+ break;
+ case CarHvacManager.HVAC_ZONED_TEMP_SETPOINT:
+ configureTempSetpoint(v);
+ break;
+ case CarHvacManager.HVAC_WINDOW_DEFROSTER_ON:
+ configureDefrosterOn(v, prop.getZone());
+ break;
+ default:
+ Log.w(TAG, "propertyId " + propId + " is not handled");
+ break;
+ }
+ }
+
+ mTvFanSpeed = (TextView) v.findViewById(R.id.tvFanSpeed);
+ mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+ mTvDTemp = (TextView) v.findViewById(R.id.tvDTemp);
+ mTvDTemp.setText(String.valueOf(mCurDTemp));
+ mTvPTemp = (TextView) v.findViewById(R.id.tvPTemp);
+ mTvPTemp.setText(String.valueOf(mCurPTemp));
+
+ if(DBG) {
+ Log.d(TAG, "Starting HvacTestFragment");
+ }
+
+ return v;
+ }
+
+ public void setHvacManager(CarHvacManager hvacManager) {
+ Log.d(TAG, "setHvacManager()");
+ mCarHvacManager = hvacManager;
+ }
+
+ private void configureAcOn(View v) {
+ mTbAc = (ToggleButton)v.findViewById(R.id.tbAc);
+ mTbAc.setEnabled(true);
+ mTbAc.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ // TODO handle zone properly
+ mCarHvacManager.setBooleanProperty(CarHvacManager.HVAC_ZONED_AC_ON, 0x8,
+ mTbAc.isChecked());
+ }
+ });
+ }
+
+ private void configureFanPosition(View v) {
+ RadioGroup rg = (RadioGroup)v.findViewById(R.id.rgFanPosition);
+ rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ switch(checkedId) {
+ case R.id.rbPositionFace:
+ mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_POSITION,
+ VehicleZone.VEHICLE_ZONE_ROW_1_ALL,
+ VehicleHvacFanDirection.VEHICLE_HVAC_FAN_DIRECTION_FACE);
+ break;
+ case R.id.rbPositionFloor:
+ mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_POSITION,
+ VehicleZone.VEHICLE_ZONE_ROW_1_ALL,
+ VehicleHvacFanDirection.VEHICLE_HVAC_FAN_DIRECTION_FLOOR);
+ break;
+ case R.id.rbPositionFaceAndFloor:
+ mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_POSITION,
+ VehicleZone.VEHICLE_ZONE_ROW_1_ALL, VehicleHvacFanDirection.
+ VEHICLE_HVAC_FAN_DIRECTION_FACE_AND_FLOOR);
+ break;
+ }
+ }
+ });
+
+ mRbFanPositionFace = (RadioButton)v.findViewById(R.id.rbPositionFace);
+ mRbFanPositionFace.setClickable(true);
+ mRbFanPositionFloor = (RadioButton)v.findViewById(R.id.rbPositionFloor);
+ mRbFanPositionFaceAndFloor = (RadioButton)v.findViewById(R.id.rbPositionFaceAndFloor);
+ mRbFanPositionFaceAndFloor.setClickable(true);
+ mRbFanPositionFloor.setClickable(true);
+ }
+
+ private void configureFanSpeed(View v) {
+ mCurFanSpeed = mCarHvacManager.getIntProperty(
+ CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ALL);
+
+ Button btnFanSpeedUp = (Button) v.findViewById(R.id.btnFanSpeedUp);
+ btnFanSpeedUp.setEnabled(true);
+ btnFanSpeedUp.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if(mCurFanSpeed < 7) {
+ mCurFanSpeed++;
+ mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+ mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ALL, mCurFanSpeed);
+ }
+ }
+ });
+
+ Button btnFanSpeedDn = (Button) v.findViewById(R.id.btnFanSpeedDn);
+ btnFanSpeedDn.setEnabled(true);
+ btnFanSpeedDn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if (mCurFanSpeed > 1) {
+ mCurFanSpeed--;
+ mTvFanSpeed.setText(String.valueOf(mCurFanSpeed));
+ mCarHvacManager.setIntProperty(CarHvacManager.HVAC_ZONED_FAN_SPEED_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ALL, mCurFanSpeed);
+ }
+ }
+ });
+ }
+
+ private void configureTempSetpoint(View v) {
+ mCurDTemp = mCarHvacManager.getFloatProperty(
+ CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ROW_1_LEFT);
+ mCurPTemp = mCarHvacManager.getFloatProperty(
+ CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ROW_1_RIGHT);
+
+ Button btnDTempUp = (Button) v.findViewById(R.id.btnDTempUp);
+ btnDTempUp.setEnabled(true);
+ btnDTempUp.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if(mCurDTemp < 29.5) {
+ mCurDTemp += 0.5;
+ mTvDTemp.setText(String.valueOf(mCurDTemp));
+ mCarHvacManager.setFloatProperty(CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ROW_1_LEFT, mCurDTemp);
+ }
+ }
+ });
+
+ Button btnDTempDn = (Button) v.findViewById(R.id.btnDTempDn);
+ btnDTempDn.setEnabled(true);
+ btnDTempDn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if(mCurDTemp > 15.5) {
+ mCurDTemp -= 0.5;
+ mTvDTemp.setText(String.valueOf(mCurDTemp));
+ mCarHvacManager.setFloatProperty(CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ROW_1_LEFT, mCurDTemp);
+ }
+ }
+ });
+
+ Button btnPTempUp = (Button) v.findViewById(R.id.btnPTempUp);
+ btnPTempUp.setEnabled(true);
+ btnPTempUp.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if (mCurPTemp < 29.5) {
+ mCurPTemp += 0.5;
+ mTvPTemp.setText(String.valueOf(mCurPTemp));
+ mCarHvacManager.setFloatProperty(CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ROW_1_RIGHT, mCurPTemp);
+ }
+ }
+ });
+
+ Button btnPTempDn = (Button) v.findViewById(R.id.btnPTempDn);
+ btnPTempDn.setEnabled(true);
+ btnPTempDn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if (mCurPTemp > 15.5) {
+ mCurPTemp -= 0.5;
+ mTvPTemp.setText(String.valueOf(mCurPTemp));
+ mCarHvacManager.setFloatProperty(CarHvacManager.HVAC_ZONED_TEMP_SETPOINT,
+ VehicleZone.VEHICLE_ZONE_ROW_1_RIGHT, mCurPTemp);
+ }
+ }
+ });
+ }
+
+ private void configureDefrosterOn(View v, int zone) {
+ if((zone & VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD) ==
+ VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD) {
+ mTbDefrostFront = (ToggleButton)v.findViewById(R.id.tbDefrostFront);
+ mTbDefrostFront.setEnabled(true);
+ mTbDefrostFront.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ mCarHvacManager.setBooleanProperty(CarHvacManager.HVAC_WINDOW_DEFROSTER_ON,
+ VehicleWindow.VEHICLE_WINDOW_FRONT_WINDSHIELD,
+ mTbDefrostFront.isChecked());
+ }
+ });
+ }
+ if((zone & VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD) ==
+ VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD) {
+ mTbDefrostRear = (ToggleButton)v.findViewById(R.id.tbDefrostRear);
+ mTbDefrostRear.setEnabled(true);
+ mTbDefrostRear.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ mCarHvacManager.setBooleanProperty(CarHvacManager.HVAC_WINDOW_DEFROSTER_ON,
+ VehicleWindow.VEHICLE_WINDOW_REAR_WINDSHIELD,
+ mTbDefrostRear.isChecked());
+ }
+ });
+ }
+ }
+}
+
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java
new file mode 100644
index 0000000..c406616
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.input;
+
+import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehiclePropValueUtil;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleHwKeyInputAction;
+import com.google.android.car.kitchensink.R;
+
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.test.CarTestManager;
+import android.car.test.CarTestManagerBinderWrapper;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnTouchListener;
+import android.widget.Button;
+
+/**
+ * Test input event handling to system.
+ * vehicle hal should have VEHICLE_PROPERTY_HW_KEY_INPUT support for this to work.
+ */
+public class InputTestFragment extends Fragment {
+
+ private static final String TAG = "CAR.INPUT.KS";
+
+ private Car mCar;
+ private CarTestManager mTestManager;
+ private Button mVolumeUp;
+ private Button mVolumeDown;
+ private Button mVoice;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.input_test, container, false);
+
+ // Single touch + key event does not work as touch is happening in other window
+ // at the same time. But long press will work.
+ mVolumeUp = (Button) view.findViewById(R.id.button_volume_up);
+ mVolumeUp.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ handleTouchEvent(event, KeyEvent.KEYCODE_VOLUME_UP);
+ return true;
+ }
+ });
+ mVolumeDown = (Button) view.findViewById(R.id.button_volume_down);
+ mVolumeDown.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ handleTouchEvent(event, KeyEvent.KEYCODE_VOLUME_DOWN);
+ return true;
+ }
+ });
+ mVoice = (Button) view.findViewById(R.id.button_voice);
+ mVoice.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ handleTouchEvent(event, KeyEvent.KEYCODE_VOICE_ASSIST);
+ return true;
+ }
+ });
+
+ mCar = Car.createCar(getContext(), new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mTestManager = new CarTestManager(
+ (CarTestManagerBinderWrapper) mCar.getCarManager(Car.TEST_SERVICE));
+ if (!mTestManager.isPropertySupported(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT)) {
+ Log.w(TAG, "VEHICLE_PROPERTY_HW_KEY_INPUT not supported");
+ mVolumeUp.setEnabled(false);
+ mVolumeDown.setEnabled(false);
+ mVoice.setEnabled(false);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ });
+ mCar.connect();
+ return view;
+ }
+
+ private void handleTouchEvent(MotionEvent event, int keyCode) {
+ int action = event.getActionMasked();
+ Log.i(TAG, "handleTouchEvent, action:" + action + ",keyCode:" + keyCode);
+ boolean shouldInject = false;
+ boolean isDown = false;
+ if (action == MotionEvent.ACTION_DOWN) {
+ shouldInject = true;
+ isDown = true;
+ } else if (action == MotionEvent.ACTION_UP) {
+ shouldInject = true;
+ }
+ if (shouldInject) {
+ int[] values = { isDown ? VehicleHwKeyInputAction.VEHICLE_HW_KEY_INPUT_ACTION_DOWN :
+ VehicleHwKeyInputAction.VEHICLE_HW_KEY_INPUT_ACTION_UP, keyCode, 0, 0 };
+ long now = SystemClock.elapsedRealtimeNanos();
+ mTestManager.injectEvent(VehiclePropValueUtil.createIntVectorValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_HW_KEY_INPUT, values, now));
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mCar.disconnect();
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/DishService.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/DishService.java
new file mode 100644
index 0000000..c2758a8
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/DishService.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.job;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.lang.ref.WeakReference;
+
+public class DishService extends JobService {
+ private static final String TAG = "JobScheduler_DishService";
+ private static final int DELAY_MS = 1000; // wash a plate every second!
+
+ private static final int MSG_FINISHED = 0;
+ private static final int MSG_RUN_JOB = 1;
+ private static final int MSG_CANCEL_JOB = 2;
+
+ public static final String EXTRA_DISH_COUNT = "dish_count";
+
+ private final Handler mHandler = new Handler() {
+ private SparseArray<JobParameters> mTaskMap = new SparseArray<JobParameters>();
+ @Override
+ public void handleMessage(Message msg) {
+ JobParameters job = (JobParameters) msg.obj;
+ switch (msg.what) {
+ case MSG_FINISHED:
+ Log.d(TAG, "Job done! " + job.getJobId());
+ mTaskMap.remove(job.getJobId());
+ jobFinished(job, false);
+ break;
+ case MSG_RUN_JOB:
+ DishWasherTask task = new DishWasherTask(this, job, msg.arg1);
+ task.execute();
+ mTaskMap.put(job.getJobId(), job);
+ break;
+ case MSG_CANCEL_JOB:
+ JobParameters job1 = mTaskMap.get(job.getJobId());
+ if (job1 != null) {
+ removeMessages(MSG_RUN_JOB, job1);
+ Log.d(TAG, "cancelled job " + job1);
+ mTaskMap.remove(job.getJobId());
+ }
+ break;
+ default:
+ Log.w(TAG, "Unknown message " + msg.what);
+ }
+ }
+ };
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ Log.d(TAG, "onStopJob " + jobParameters);
+ Message msg = mHandler.obtainMessage(MSG_CANCEL_JOB, 0, 0, jobParameters);
+ mHandler.sendMessage(msg);
+ return false;
+ }
+
+ @Override
+ public boolean onStartJob(final JobParameters jobParameters) {
+ Log.d(TAG, "onStartJob " + jobParameters);
+ Message msg = mHandler.obtainMessage(MSG_RUN_JOB, 0, 0, jobParameters);
+ mHandler.sendMessage(msg);
+ return true;
+ }
+
+ private static final class DishWasherTask extends AsyncTask<Void, Void, Boolean> {
+ private final WeakReference<Handler> mHandler;
+ private final JobParameters mJobParameter;
+ private final int mMyDishNum;
+
+
+ public DishWasherTask(Handler handler, JobParameters jobParameters, int dishNum) {
+ mHandler = new WeakReference<Handler>(handler);
+ mJobParameter = jobParameters;
+ mMyDishNum = dishNum;
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... infos) {
+ int dishTotal = mJobParameter.getExtras().getInt(EXTRA_DISH_COUNT);
+
+ Log.d(TAG, "jobId: " + mJobParameter.getJobId() + " totalDish: " + dishTotal
+ + " washing: #" + mMyDishNum);
+ wash();
+ if (mMyDishNum >= dishTotal - 1) {
+ // all done!
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (mHandler.get() == null) {
+ return;
+ }
+ if (result) {
+ Message msg = mHandler.get().obtainMessage(MSG_RUN_JOB,
+ mMyDishNum +1, 0, mJobParameter);
+ mHandler.get().sendMessageDelayed(msg, DELAY_MS);
+ } else {
+ Message msg = mHandler.get().obtainMessage(MSG_FINISHED, 0,
+ 0, mJobParameter);
+ mHandler.get().sendMessage(msg);
+ }
+ }
+
+ private void wash() {
+ // TODO: add heavy wash tasks here...
+ }
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/JobSchedulerFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/JobSchedulerFragment.java
new file mode 100644
index 0000000..3cf96e4
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/job/JobSchedulerFragment.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.job;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.support.annotation.Nullable;
+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.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.List;
+
+public class JobSchedulerFragment extends Fragment {
+ private static final String TAG = "JobSchedulerFragment";
+ private static final String PREFS_NEXT_JOB_ID = "next_job_id";
+
+ private Button mScheduleButton;
+ private Button mRefreshButton;
+ private Button mCancelButton;
+ private CheckBox mRequireCharging;
+ private CheckBox mRequireIdle;
+ private CheckBox mRequirePersisted;
+ private RadioGroup mNetworkGroup;
+ private EditText mDishNum;
+ private TextView mJobInfo;
+ private JobScheduler mJobScheduler;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.job_scheduler, container, false);
+ mScheduleButton = (Button) v.findViewById(R.id.schedule_button);
+ mScheduleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ scheduleJob();
+ }
+ });
+ mRefreshButton = (Button) v.findViewById(R.id.refresh_button);
+ mRefreshButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ refreshCurrentJobs();
+ }
+ });
+
+ mNetworkGroup = (RadioGroup) v.findViewById(R.id.network_group);
+ mDishNum = (EditText) v.findViewById(R.id.dish_num);
+ mRequireCharging = (CheckBox) v.findViewById(R.id.require_charging);
+ mRequireIdle = (CheckBox) v.findViewById(R.id.require_idle);
+ mRequirePersisted = (CheckBox) v.findViewById(R.id.require_persisted);
+ mJobScheduler = (JobScheduler) getContext()
+ .getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+ mJobInfo = (TextView) v.findViewById(R.id.current_jobs);
+
+ mCancelButton = (Button) v.findViewById(R.id.cancel_button);
+ mCancelButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mJobScheduler.cancelAll();
+ refreshCurrentJobs();
+ }
+ });
+ return v;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refreshCurrentJobs();
+ }
+
+ private void refreshCurrentJobs() {
+ StringBuilder sb = new StringBuilder();
+ List<JobInfo> jobs = mJobScheduler.getAllPendingJobs();
+ for (JobInfo job : jobs) {
+ sb.append("JobId: ");
+ sb.append(job.getId());
+ sb.append("\nDishCount: ");
+ sb.append(job.getExtras().getInt(DishService.EXTRA_DISH_COUNT, 0));
+ sb.append("\n");
+ }
+ mJobInfo.setText(sb.toString());
+ }
+
+ private void scheduleJob() {
+ ComponentName jobComponentName = new ComponentName(getContext(), DishService.class);
+ SharedPreferences prefs = getContext()
+ .getSharedPreferences(PREFS_NEXT_JOB_ID, Context.MODE_PRIVATE);
+ int jobId = prefs.getInt(PREFS_NEXT_JOB_ID, 0);
+ PersistableBundle bundle = new PersistableBundle();
+ int count = 50;
+ try {
+ count = Integer.valueOf(mDishNum.getText().toString());
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "NOT A NUMBER!!!");
+ }
+
+ int selected = mNetworkGroup.getCheckedRadioButtonId();
+ int networkType = JobInfo.NETWORK_TYPE_ANY;
+ switch (selected) {
+ case R.id.network_none:
+ networkType = JobInfo.NETWORK_TYPE_NONE;
+ break;
+ case R.id.network_unmetered:
+ networkType = JobInfo.NETWORK_TYPE_UNMETERED;
+ break;
+ case R.id.network_any:
+ networkType = JobInfo.NETWORK_TYPE_ANY;
+ break;
+ }
+ bundle.putInt(DishService.EXTRA_DISH_COUNT, count);
+ JobInfo jobInfo = new JobInfo.Builder(jobId, jobComponentName)
+ .setRequiresCharging(mRequireCharging.isChecked())
+ .setRequiresDeviceIdle(mRequireIdle.isChecked())
+ // TODO: figure out why we crash here even we hold
+ // the RECEIVE_BOOT_COMPLETE permission
+ //.setPersisted(mRequirePersisted.isChecked())
+ .setExtras(bundle)
+ .setRequiredNetworkType(networkType)
+ .build();
+
+
+ mJobScheduler.schedule(jobInfo);
+ Toast.makeText(getContext(), "Scheduled: " + jobInfo, Toast.LENGTH_LONG ).show();
+
+ Log.d(TAG, "Scheduled a job: " + jobInfo);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PREFS_NEXT_JOB_ID, jobId + 1);
+ editor.commit();
+
+ refreshCurrentJobs();
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/keyboard/KeyboardFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/keyboard/KeyboardFragment.java
new file mode 100644
index 0000000..4ee0b16
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/keyboard/KeyboardFragment.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.keyboard;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.support.car.app.menu.CarDrawerActivity;
+import android.support.car.app.menu.SearchBoxEditListener;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.google.android.car.kitchensink.R;
+
+public class KeyboardFragment extends Fragment {
+ public static final int CARD = 0xfffafafa;
+ public static final int TEXT_PRIMARY_DAY = 0xde000000;
+ public static final int TEXT_SECONDARY_DAY = 0x8a000000;
+
+ private Button mImeButton;
+ private Button mCloseImeButton;
+ private Button mShowHideInputButton;
+ private CarDrawerActivity mActivity;
+ private TextView mOnSearchText;
+ private TextView mOnEditText;
+
+ private final Handler mHandler = new Handler();
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.keyboard_test, container, false);
+ mActivity = (CarDrawerActivity) getHost();
+ mImeButton = (Button) v.findViewById(R.id.ime_button);
+ mImeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mActivity.startInput("Hint");
+ }
+ });
+
+ mCloseImeButton = (Button) v.findViewById(R.id.stop_ime_button);
+ mCloseImeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mActivity.stopInput();
+ resetInput();
+ }
+ });
+
+ mShowHideInputButton = (Button) v.findViewById(R.id.ime_button2);
+ mShowHideInputButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mActivity.isShowingSearchBox()) {
+ mActivity.hideSearchBox();
+ } else {
+ resetInput();
+ }
+ }
+ });
+
+ mOnSearchText = (TextView) v.findViewById(R.id.search_text);
+ mOnEditText = (TextView) v.findViewById(R.id.edit_text);
+ resetInput();
+ mActivity.setSearchBoxEndView(View.inflate(getContext(), R.layout.keyboard_end_view, null));
+
+ return v;
+ }
+
+ private void resetInput() {
+ mActivity.showSearchBox(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mActivity.startInput("Hint");
+ }
+ });
+ mActivity.setSearchBoxEditListener(mEditListener);
+ mActivity.setSearchBoxColors(CARD, TEXT_SECONDARY_DAY,
+ TEXT_PRIMARY_DAY, TEXT_SECONDARY_DAY);
+ }
+
+
+ private final SearchBoxEditListener mEditListener = new SearchBoxEditListener() {
+ @Override
+ public void onSearch(final String text) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnSearchText.setText("Search: " + text);
+ resetInput();
+ }
+ });
+ }
+
+ @Override
+ public void onEdit(final String text) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnEditText.setText("Edit: " + text);
+ }
+ });
+ }
+ };
+}
diff --git a/tests/api_test/Android.mk b/tests/android_car_api_test/Android.mk
similarity index 90%
copy from tests/api_test/Android.mk
copy to tests/android_car_api_test/Android.mk
index e9d2827..aec4c71 100644
--- a/tests/api_test/Android.mk
+++ b/tests/android_car_api_test/Android.mk
@@ -20,7 +20,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := CarApiTest
+LOCAL_PACKAGE_NAME := AndroidCarApiTest
# for system|priviledged permission.
LOCAL_CERTIFICATE := platform
@@ -32,7 +32,7 @@
LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_STATIC_JAVA_LIBRARIES += android.support.car car-systemtest
+LOCAL_STATIC_JAVA_LIBRARIES += car-systemtest
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
diff --git a/tests/api_test/AndroidManifest.xml b/tests/android_car_api_test/AndroidManifest.xml
similarity index 100%
rename from tests/api_test/AndroidManifest.xml
rename to tests/android_car_api_test/AndroidManifest.xml
diff --git a/tests/api_test/src/com/android/support/car/apitest/AppBlockingPackageInfoTest.java b/tests/android_car_api_test/src/com/android/car/apitest/AppBlockingPackageInfoTest.java
similarity index 98%
rename from tests/api_test/src/com/android/support/car/apitest/AppBlockingPackageInfoTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/AppBlockingPackageInfoTest.java
index 1f29551..5fe56fe 100644
--- a/tests/api_test/src/com/android/support/car/apitest/AppBlockingPackageInfoTest.java
+++ b/tests/android_car_api_test/src/com/android/car/apitest/AppBlockingPackageInfoTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.support.car.apitest;
+package com.android.car.apitest;
import android.car.content.pm.AppBlockingPackageInfo;
import android.content.Context;
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarApiTestBase.java b/tests/android_car_api_test/src/com/android/car/apitest/CarApiTestBase.java
new file mode 100644
index 0000000..d253ae2
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarApiTestBase.java
@@ -0,0 +1,77 @@
+/*
+ * 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.car.apitest;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.Looper;
+import android.car.Car;
+import android.test.AndroidTestCase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CarApiTestBase extends AndroidTestCase {
+ protected static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
+
+ private Car mCar;
+
+ private final DefaultServiceConnectionListener mConnectionListener =
+ new DefaultServiceConnectionListener();
+
+ protected void assertMainThread() {
+ assertTrue(Looper.getMainLooper().isCurrentThread());
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCar = Car.createCar(getContext(), mConnectionListener);
+ mCar.connect();
+ mConnectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mCar.disconnect();
+ }
+
+ protected synchronized Car getCar() {
+ return mCar;
+ }
+
+ protected class DefaultServiceConnectionListener implements ServiceConnection {
+ private final Semaphore mConnectionWait = new Semaphore(0);
+
+ public void waitForConnection(long timeoutMs) throws InterruptedException {
+ mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ assertMainThread();
+ mConnectionWait.release();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ assertMainThread();
+ }
+ }
+}
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarAppBlockingPolicyTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarAppBlockingPolicyTest.java
similarity index 97%
rename from tests/api_test/src/com/android/support/car/apitest/CarAppBlockingPolicyTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/CarAppBlockingPolicyTest.java
index c1a1e8e..633a93c 100644
--- a/tests/api_test/src/com/android/support/car/apitest/CarAppBlockingPolicyTest.java
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarAppBlockingPolicyTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.support.car.apitest;
+package com.android.car.apitest;
import android.car.content.pm.AppBlockingPackageInfo;
import android.car.content.pm.CarAppBlockingPolicy;
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java
new file mode 100644
index 0000000..6f73e47
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java
@@ -0,0 +1,231 @@
+/*
+ * 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.car.apitest;
+
+import android.car.Car;
+import android.car.CarAppContextManager;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CarAppContextManagerTest extends CarApiTestBase {
+ private static final String TAG = CarAppContextManager.class.getSimpleName();
+ private CarAppContextManager mManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mManager = (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
+ assertNotNull(mManager);
+ }
+
+ public void testUnregisteredAccess() throws Exception {
+ try {
+ mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ fail();
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ }
+
+ public void testRegisterNull() throws Exception {
+ try {
+ mManager.registerContextListener(null, 0);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testRegisterUnregister() throws Exception {
+ ContextChangeListerner listener = new ContextChangeListerner();
+ ContextChangeListerner listener2 = new ContextChangeListerner();
+ mManager.registerContextListener(listener, 0);
+ mManager.registerContextListener(listener2, 0);
+ mManager.unregisterContextListener();
+ // this one is no-op
+ mManager.unregisterContextListener();
+ }
+
+ public void testContextChange() throws Exception {
+ DefaultServiceConnectionListener connectionListener =
+ new DefaultServiceConnectionListener();
+ Car car2 = Car.createCar(getContext(), connectionListener, null);
+ car2.connect();
+ connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ CarAppContextManager manager2 = (CarAppContextManager)
+ car2.getCarManager(Car.APP_CONTEXT_SERVICE);
+ assertNotNull(manager2);
+
+ assertEquals(0, mManager.getActiveAppContexts());
+ ContextChangeListerner owner = new ContextChangeListerner();
+ ContextChangeListerner owner2 = new ContextChangeListerner();
+ mManager.registerContextListener(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION |
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ manager2.registerContextListener(owner2, CarAppContextManager.APP_CONTEXT_NAVIGATION |
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+
+ mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ int expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
+ assertEquals(expectedContexts, mManager.getActiveAppContexts());
+ assertEquals(expectedContexts, manager2.getActiveAppContexts());
+ assertTrue(mManager.isOwningContext(expectedContexts));
+ assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertTrue(owner2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ expectedContexts));
+ // owner should not get notification for its own change
+ assertFalse(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+
+ mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION |
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND;
+ assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertEquals(expectedContexts, mManager.getActiveAppContexts());
+ assertEquals(expectedContexts, manager2.getActiveAppContexts());
+ assertTrue(owner2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ expectedContexts));
+ // owner should not get notification for its own change
+ assertFalse(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+
+ // this should be no-op
+ mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ assertEquals(expectedContexts, mManager.getActiveAppContexts());
+ assertEquals(expectedContexts, manager2.getActiveAppContexts());
+ assertFalse(owner2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+ assertFalse(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+
+ manager2.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertEquals(expectedContexts, mManager.getActiveAppContexts());
+ assertEquals(expectedContexts, manager2.getActiveAppContexts());
+ assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppContextManager.APP_CONTEXT_NAVIGATION));
+
+ // no-op as it is not owning it
+ mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertEquals(expectedContexts, mManager.getActiveAppContexts());
+ assertEquals(expectedContexts, manager2.getActiveAppContexts());
+
+ mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
+ assertEquals(expectedContexts, mManager.getActiveAppContexts());
+ assertEquals(expectedContexts, manager2.getActiveAppContexts());
+ assertTrue(owner2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+
+ manager2.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
+ expectedContexts = 0;
+ assertEquals(expectedContexts, mManager.getActiveAppContexts());
+ assertEquals(expectedContexts, manager2.getActiveAppContexts());
+ assertTrue(owner.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+ mManager.unregisterContextListener();
+ manager2.unregisterContextListener();
+ }
+
+ public void testFilter() throws Exception {
+ DefaultServiceConnectionListener connectionListener =
+ new DefaultServiceConnectionListener();
+ Car car2 = Car.createCar(getContext(), connectionListener);
+ car2.connect();
+ connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ CarAppContextManager manager2 = (CarAppContextManager)
+ car2.getCarManager(Car.APP_CONTEXT_SERVICE);
+ assertNotNull(manager2);
+
+ assertEquals(0, mManager.getActiveAppContexts());
+ ContextChangeListerner owner = new ContextChangeListerner();
+ ContextChangeListerner listener = new ContextChangeListerner();
+ mManager.registerContextListener(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION |
+ CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ manager2.registerContextListener(listener, CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ mManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+ mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+ mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
+ }
+
+ private class ContextChangeListerner implements CarAppContextManager.AppContextChangeListener {
+ private int mLastChangeEvent;
+ private final Semaphore mChangeWait = new Semaphore(0);
+ private int mLastLossEvent;
+ private final Semaphore mLossEventWait = new Semaphore(0);
+
+ public boolean waitForContextChangeAndAssert(long timeoutMs, int expectedContexts)
+ throws Exception {
+ if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedContexts, mLastChangeEvent);
+ return true;
+ }
+
+ public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedContexts)
+ throws Exception {
+ if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedContexts, mLastLossEvent);
+ return true;
+ }
+
+ @Override
+ public void onAppContextChange(int activeContexts) {
+ Log.i(TAG, "onAppContextChange " + Integer.toHexString(activeContexts));
+ assertMainThread();
+ mLastChangeEvent = activeContexts;
+ mChangeWait.release();
+ }
+
+ @Override
+ public void onAppContextOwnershipLoss(int context) {
+ Log.i(TAG, "onAppContextOwnershipLoss " + Integer.toHexString(context));
+ assertMainThread();
+ mLastLossEvent = context;
+ mLossEventWait.release();
+ }
+ }
+}
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java
new file mode 100644
index 0000000..3f74a8f
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.apitest;
+
+import android.os.Looper;
+import android.car.Car;
+import android.car.CarAppContextManager;
+import android.car.CarAppContextManager.AppContextChangeListener;
+import android.car.navigation.CarNavigationInstrumentCluster;
+import android.car.CarNotConnectedException;
+import android.car.navigation.CarNavigationManager;
+import android.car.navigation.CarNavigationManager.CarNavigationListener;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for {@link android.support.car.navigation.CarNavigationStatusManager}
+ */
+public class CarNavigationManagerTest extends CarApiTestBase {
+
+ private CarNavigationManager mCarNavigationManager;
+ private CarAppContextManager mCarAppContextManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCarNavigationManager =
+ (CarNavigationManager) getCar().getCarManager(Car.CAR_NAVIGATION_SERVICE);
+ assertNotNull(mCarNavigationManager);
+ mCarAppContextManager =
+ (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
+ assertNotNull(mCarAppContextManager);
+ }
+
+ public void testStart() throws Exception {
+ final CountDownLatch onStartLatch = new CountDownLatch(1);
+
+ mCarNavigationManager.registerListener(new CarNavigationListener() {
+ @Override
+ public void onInstrumentClusterStart(CarNavigationInstrumentCluster instrumentCluster) {
+ // TODO: we should use VehicleHalMock once we implement HAL support in
+ // CarNavigationStatusService.
+ assertFalse(instrumentCluster.supportsCustomImages());
+ assertEquals(1000, instrumentCluster.getMinIntervalMs());
+ onStartLatch.countDown();
+ }
+
+ @Override
+ public void onInstrumentClusterStop() {
+ // TODO
+ }
+ });
+
+ assertTrue(onStartLatch.await(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ try {
+ mCarNavigationManager.sendNavigationStatus(1);
+ fail();
+ } catch (IllegalStateException expected) {
+ // Expected. Client should acquire context ownership for APP_CONTEXT_NAVIGATION.
+ }
+
+ mCarAppContextManager.registerContextListener(new AppContextChangeListener() {
+ @Override
+ public void onAppContextChange(int activeContexts) {
+ // Nothing to do here.
+ }
+
+ @Override
+ public void onAppContextOwnershipLoss(int context) {
+ // Nothing to do here.
+ }
+ }, CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mCarAppContextManager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ assertTrue(mCarAppContextManager.isOwningContext(
+ CarAppContextManager.APP_CONTEXT_NAVIGATION));
+
+ // TODO: we should use mocked HAL to be able to verify this, right now just make sure that
+ // it is not crashing and logcat has appropriate traces.
+ mCarNavigationManager.sendNavigationStatus(1);
+ }
+}
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarPackageManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarPackageManagerTest.java
new file mode 100644
index 0000000..bffb526
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarPackageManagerTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.car.apitest;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.Looper;
+import android.car.Car;
+import android.car.content.pm.CarPackageManager;
+import android.test.AndroidTestCase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CarPackageManagerTest extends AndroidTestCase {
+ private static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
+
+ private final Semaphore mConnectionWait = new Semaphore(0);
+
+ private Car mCar;
+ private CarPackageManager mCarPackageManager;
+
+ private final ServiceConnection mConnectionListener = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ assertMainThread();
+ mConnectionWait.release();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ assertMainThread();
+ }
+
+ };
+
+ private void assertMainThread() {
+ assertTrue(Looper.getMainLooper().isCurrentThread());
+ }
+ private void waitForConnection(long timeoutMs) throws InterruptedException {
+ mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCar = Car.createCar(getContext(), mConnectionListener);
+ mCar.connect();
+ waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ mCarPackageManager = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
+ assertNotNull(mCarPackageManager);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mCar.disconnect();
+ }
+
+ public void testCreate() throws Exception {
+ //nothing to do for now
+ }
+}
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarSensorManagerTest.java
similarity index 83%
rename from tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/CarSensorManagerTest.java
index a608284..6175fc4 100644
--- a/tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarSensorManagerTest.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.support.car.apitest;
+package com.android.car.apitest;
import android.content.ComponentName;
+import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Looper;
-import android.support.car.Car;
-import android.support.car.ServiceConnectionListener;
-import android.support.car.hardware.CarSensorEvent;
-import android.support.car.hardware.CarSensorManager;
+import android.car.Car;
+import android.car.hardware.CarSensorEvent;
+import android.car.hardware.CarSensorManager;
import android.test.AndroidTestCase;
import java.util.concurrent.Semaphore;
@@ -37,12 +37,7 @@
private Car mCar;
private CarSensorManager mCarSensorManager;
- private final ServiceConnectionListener mConnectionListener = new ServiceConnectionListener() {
-
- @Override
- public void onServiceSuspended(int cause) {
- assertMainThread();
- }
+ private final ServiceConnection mConnectionListener = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
@@ -50,11 +45,6 @@
}
@Override
- public void onServiceConnectionFailed(int cause) {
- assertMainThread();
- }
-
- @Override
public void onServiceConnected(ComponentName name, IBinder service) {
assertMainThread();
mConnectionWait.release();
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarTest.java
new file mode 100644
index 0000000..a82a4fd
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.car.apitest;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.Looper;
+import android.car.Car;
+import android.car.hardware.CarSensorManager;
+import android.test.AndroidTestCase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+
+public class CarTest extends AndroidTestCase {
+ private static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
+
+ private final Semaphore mConnectionWait = new Semaphore(0);
+
+ private final ServiceConnection mConnectionListener = new ServiceConnection() {
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ assertMainThread();
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ assertMainThread();
+ mConnectionWait.release();
+ }
+ };
+
+ private void assertMainThread() {
+ assertTrue(Looper.getMainLooper().isCurrentThread());
+ }
+
+ private void waitForConnection(long timeoutMs) throws InterruptedException {
+ mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+
+ public void testCarConnection() throws Exception {
+ Car car = Car.createCar(getContext(), mConnectionListener);
+ assertFalse(car.isConnected());
+ assertFalse(car.isConnecting());
+ car.connect();
+ //TODO fix race here
+ assertTrue(car.isConnecting());
+ waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ assertTrue(car.isConnected());
+ assertFalse(car.isConnecting());
+ CarSensorManager carSensorManager =
+ (CarSensorManager) car.getCarManager(Car.SENSOR_SERVICE);
+ assertNotNull(carSensorManager);
+ CarSensorManager carSensorManager2 =
+ (CarSensorManager) car.getCarManager(Car.SENSOR_SERVICE);
+ assertEquals(carSensorManager, carSensorManager2);
+ Object noSuchService = car.getCarManager("No such service");
+ assertNull(noSuchService);
+ // double disconnect should be safe.
+ car.disconnect();
+ car.disconnect();
+ assertFalse(car.isConnected());
+ assertFalse(car.isConnecting());
+ }
+
+ public void testDoubleConnect() throws Exception {
+ Car car = Car.createCar(getContext(), mConnectionListener);
+ assertFalse(car.isConnected());
+ assertFalse(car.isConnecting());
+ car.connect();
+ try {
+ car.connect();
+ fail("dobule connect should throw");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ car.disconnect();
+ }
+}
diff --git a/tests/api_test/src/com/android/car/apitest/VehicleDoorTest.java b/tests/android_car_api_test/src/com/android/car/apitest/VehicleDoorTest.java
similarity index 100%
rename from tests/api_test/src/com/android/car/apitest/VehicleDoorTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/VehicleDoorTest.java
diff --git a/tests/api_test/src/com/android/car/apitest/VehicleSeatTest.java b/tests/android_car_api_test/src/com/android/car/apitest/VehicleSeatTest.java
similarity index 100%
rename from tests/api_test/src/com/android/car/apitest/VehicleSeatTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/VehicleSeatTest.java
diff --git a/tests/api_test/src/com/android/car/apitest/VehicleWindowTest.java b/tests/android_car_api_test/src/com/android/car/apitest/VehicleWindowTest.java
similarity index 100%
rename from tests/api_test/src/com/android/car/apitest/VehicleWindowTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/VehicleWindowTest.java
diff --git a/tests/api_test/src/com/android/car/apitest/VehicleZoneTest.java b/tests/android_car_api_test/src/com/android/car/apitest/VehicleZoneTest.java
similarity index 100%
rename from tests/api_test/src/com/android/car/apitest/VehicleZoneTest.java
rename to tests/android_car_api_test/src/com/android/car/apitest/VehicleZoneTest.java
diff --git a/tests/api_test/Android.mk b/tests/android_support_car_api_test/Android.mk
similarity index 90%
rename from tests/api_test/Android.mk
rename to tests/android_support_car_api_test/Android.mk
index e9d2827..f74fb44 100644
--- a/tests/api_test/Android.mk
+++ b/tests/android_support_car_api_test/Android.mk
@@ -20,7 +20,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := CarApiTest
+LOCAL_PACKAGE_NAME := AndroudSupportCarApiTest
# for system|priviledged permission.
LOCAL_CERTIFICATE := platform
@@ -32,7 +32,7 @@
LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_STATIC_JAVA_LIBRARIES += android.support.car car-systemtest
+LOCAL_STATIC_JAVA_LIBRARIES += android.support.car
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
diff --git a/tests/api_test/AndroidManifest.xml b/tests/android_support_car_api_test/AndroidManifest.xml
similarity index 100%
copy from tests/api_test/AndroidManifest.xml
copy to tests/android_support_car_api_test/AndroidManifest.xml
diff --git a/tests/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
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarActivityTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarActivityTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarApiTestBase.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarApiTestBase.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarApiTestBase.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarApiTestBase.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarConnectionListenerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarConnectionListenerTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarConnectionListenerTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarConnectionListenerTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarPackageManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarPackageManagerTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarPackageManagerTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarPackageManagerTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
similarity index 68%
copy from tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
copy to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
index a608284..e30afe0 100644
--- a/tests/api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
+++ b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
@@ -103,4 +103,29 @@
CarSensorManager.SENSOR_TYPE_DRIVING_STATUS);
assertNotNull(lastEvent);
}
+
+ public void testSensorType() throws Exception {
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_CAR_SPEED,
+ CarSensorManager.SENSOR_TYPE_CAR_SPEED);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
+ CarSensorManager.SENSOR_TYPE_DRIVING_STATUS);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_ENVIRONMENT,
+ CarSensorManager.SENSOR_TYPE_ENVIRONMENT);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_FUEL_LEVEL,
+ CarSensorManager.SENSOR_TYPE_FUEL_LEVEL);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_GEAR,
+ CarSensorManager.SENSOR_TYPE_GEAR);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_NIGHT,
+ CarSensorManager.SENSOR_TYPE_NIGHT);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_ODOMETER,
+ CarSensorManager.SENSOR_TYPE_ODOMETER);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_PARKING_BRAKE,
+ CarSensorManager.SENSOR_TYPE_PARKING_BRAKE);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_RPM,
+ CarSensorManager.SENSOR_TYPE_RPM);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_START,
+ CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_START);
+ assertEquals(android.car.hardware.CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_END,
+ CarSensorManager.SENSOR_TYPE_VENDOR_EXTENSION_END);
+ }
}
diff --git a/tests/api_test/src/com/android/support/car/apitest/CarTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/CarTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/CarTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/TestAction.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestAction.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/TestAction.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/TestAction.java
diff --git a/tests/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
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/TestCarActivity.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarActivity.java
diff --git a/tests/api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
similarity index 100%
rename from tests/api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
rename to tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
diff --git a/tests/car_activity_test_app/AndroidManifest.xml b/tests/car_activity_test_app/AndroidManifest.xml
deleted file mode 100644
index 5564954..0000000
--- a/tests/car_activity_test_app/AndroidManifest.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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="com.android.support.car.test.caractivitytest" >
- <uses-sdk
- android:minSdkVersion="22"
- android:targetSdkVersion='23'/>
- <uses-permission android:name="android.car.permission.CAR_SPEED" />
- <application android:label="CarActivityTest"
- android:icon="@drawable/ic_launcher">
- <activity android:name=".HelloCarProxyActivity"
- android:theme="@android:style/Theme.Holo.NoActionBar.TranslucentDecor"
- android:label="HelloCarActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/car_activity_test_app/res/drawable-hdpi/ic_cheese.png b/tests/car_activity_test_app/res/drawable-hdpi/ic_cheese.png
deleted file mode 100644
index 6577aee..0000000
--- a/tests/car_activity_test_app/res/drawable-hdpi/ic_cheese.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-hdpi/ic_launcher.png b/tests/car_activity_test_app/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 288b665..0000000
--- a/tests/car_activity_test_app/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-mdpi/ic_cheese.png b/tests/car_activity_test_app/res/drawable-mdpi/ic_cheese.png
deleted file mode 100644
index d24a3bc..0000000
--- a/tests/car_activity_test_app/res/drawable-mdpi/ic_cheese.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-mdpi/ic_launcher.png b/tests/car_activity_test_app/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 6ae570b..0000000
--- a/tests/car_activity_test_app/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-xhdpi/ic_cheese.png b/tests/car_activity_test_app/res/drawable-xhdpi/ic_cheese.png
deleted file mode 100644
index 485353c..0000000
--- a/tests/car_activity_test_app/res/drawable-xhdpi/ic_cheese.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-xhdpi/ic_launcher.png b/tests/car_activity_test_app/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index d4fb7cd..0000000
--- a/tests/car_activity_test_app/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-xxhdpi/ic_cheese.png b/tests/car_activity_test_app/res/drawable-xxhdpi/ic_cheese.png
deleted file mode 100644
index a99571a..0000000
--- a/tests/car_activity_test_app/res/drawable-xxhdpi/ic_cheese.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/drawable-xxhdpi/ic_launcher.png b/tests/car_activity_test_app/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 85a6081..0000000
--- a/tests/car_activity_test_app/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/tests/car_activity_test_app/res/layout/hello_caractivity.xml b/tests/car_activity_test_app/res/layout/hello_caractivity.xml
deleted file mode 100644
index 02df7c9..0000000
--- a/tests/car_activity_test_app/res/layout/hello_caractivity.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="100dp"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/textView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textSize="30dp"
- android:text="@string/app_title" />
- <TextView
- android:id="@+id/textView_dring_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <TextView
- android:id="@+id/textView_gear"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <TextView
- android:id="@+id/textView_speed"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <TextView
- android:id="@+id/textView_parking_brake"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-</LinearLayout>
diff --git a/tests/car_activity_test_app/res/values/strings.xml b/tests/car_activity_test_app/res/values/strings.xml
deleted file mode 100644
index c7093e9..0000000
--- a/tests/car_activity_test_app/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="app_title">Android Auto Embedded App</string>
- <string name="button1">Button</string>
-</resources>
diff --git a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/Cheeses.java b/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/Cheeses.java
deleted file mode 100644
index 9d632f0..0000000
--- a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/Cheeses.java
+++ /dev/null
@@ -1,153 +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.test.caractivitytest;
-
-public class Cheeses {
-
- public static final String[] ITEMS = {
- "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
- "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
- "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
- "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
- "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
- "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
- "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
- "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
- "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
- "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
- "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
- "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
- "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
- "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
- "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
- "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
- "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
- "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
- "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
- "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
- "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
- "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
- "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
- "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
- "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
- "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
- "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
- "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
- "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
- "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
- "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
- "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
- "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
- "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
- "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
- "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
- "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
- "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
- "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
- "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
- "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
- "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
- "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
- "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
- "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
- "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
- "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
- "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
- "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
- "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
- "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
- "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
- "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
- "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
- "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
- "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
- "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
- "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
- "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
- "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
- "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
- "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
- "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
- "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
- "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
- "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
- "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
- "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
- "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
- "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
- "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
- "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
- "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
- "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
- "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
- "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
- "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
- "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
- "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
- "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
- "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
- "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
- "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
- "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
- "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
- "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
- "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
- "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
- "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
- "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
- "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
- "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
- "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
- "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
- "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
- "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
- "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
- "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
- "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
- "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
- "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
- "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
- "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
- "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
- "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
- "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
- "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
- "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
- "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
- "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
- "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
- "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
- "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
- "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
- "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
- "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
- "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
- "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
- "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
- "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
- "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
- "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
- "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
- "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
- "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
- "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
- "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
- "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
- "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
- "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
- };
-}
diff --git a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java b/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java
deleted file mode 100644
index 3dcb489..0000000
--- a/tests/car_activity_test_app/src/com/android/support/car/test/caractivitytest/HelloCarActivity.java
+++ /dev/null
@@ -1,324 +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.test.caractivitytest;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.support.car.Car;
-import android.support.car.CarNotConnectedException;
-import android.support.car.ServiceConnectionListener;
-import android.support.car.app.menu.CarDrawerActivity;
-import android.support.car.app.menu.CarMenu;
-import android.support.car.app.menu.CarMenuCallbacks;
-import android.support.car.app.menu.Root;
-import android.support.car.hardware.CarSensorEvent;
-import android.support.car.hardware.CarSensorManager;
-import android.util.Log;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class HelloCarActivity extends CarDrawerActivity {
- private static final String TAG = "HelloCarActivity";
- private TextView mTextView1;
- private TextView mTextViewDrivingStatus;
- private TextView mTextViewGear;
- private TextView mTextViewParkingBrake;
- private TextView mTextViewSpeed;
- private Car mCarApi;
- private CarSensorManager mCarSensorManager;
- private final Handler mHandler = new Handler();
- private final CarSensorManager.CarSensorEventListener mListener =
- new CarSensorManager.CarSensorEventListener() {
- @Override
- public void onSensorChanged(CarSensorEvent event) {
- switch (event.sensorType) {
- case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
- mTextViewSpeed.setText("speed:" + event.floatValues[0]);
- break;
- case CarSensorManager.SENSOR_TYPE_GEAR:
- mTextViewGear.setText("gear:" + event.intValues[0]);
- break;
- case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
- mTextViewParkingBrake.setText("parking brake:" + event.intValues[0]);
- break;
- case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
- mTextViewDrivingStatus.setText("driving status:" + event.intValues[0]);
- break;
- }
- }
- };
-
- public HelloCarActivity(Proxy proxy, Context context, Car car) {
- super(proxy, context, car);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- resetTitle();
- setScrimColor(Color.RED);
- setLightMode();
- setCarMenuCallbacks(new MyCarMenuCallbacks());
- setContentView(R.layout.hello_caractivity);
- mTextView1 = (TextView) findViewById(R.id.textView1);
- mTextViewDrivingStatus = (TextView) findViewById(R.id.textView_dring_status);
- mTextViewGear = (TextView) findViewById(R.id.textView_gear);
- mTextViewParkingBrake = (TextView) findViewById(R.id.textView_parking_brake);
- mTextViewSpeed = (TextView) findViewById(R.id.textView_speed);
- mCarApi = Car.createCar(getContext(), mServiceConnectionListener);
- // Connection to Car Service does not work for non-automotive yet.
- if (mCarApi != null) {
- mCarApi.connect();
- }
- Log.i(TAG, "onCreate");
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- Log.i(TAG, "onStart");
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
- Log.i(TAG, "onRestart");
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample_apps_bg);
- setBackground(bitmap);
- Log.i(TAG, "onResume");
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- Log.i(TAG, "onPause");
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- Log.i(TAG, "onStop");
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mCarSensorManager != null) {
- mCarSensorManager.unregisterListener(mListener);
- }
- if (mCarApi != null) {
- mCarApi.disconnect();
- }
- Log.i(TAG, "onDestroy");
- }
-
- private void resetTitle() {
- setTitle(getContext().getString(R.string.app_title));
- }
-
- private final ServiceConnectionListener mServiceConnectionListener = new ServiceConnectionListener() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "Connected to Car Service");
- try {
- mCarSensorManager = (CarSensorManager) mCarApi.getCarManager(Car.SENSOR_SERVICE);
- mCarSensorManager.registerListener(mListener,
- CarSensorManager.SENSOR_TYPE_CAR_SPEED,
- CarSensorManager.SENSOR_RATE_NORMAL);
- mCarSensorManager.registerListener(mListener,
- CarSensorManager.SENSOR_TYPE_GEAR,
- CarSensorManager.SENSOR_RATE_NORMAL);
- mCarSensorManager.registerListener(mListener,
- CarSensorManager.SENSOR_TYPE_PARKING_BRAKE,
- CarSensorManager.SENSOR_RATE_NORMAL);
- mCarSensorManager.registerListener(mListener,
- CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
- CarSensorManager.SENSOR_RATE_NORMAL);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!");
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "Disconnect from Car Service");
- }
-
- @Override
- public void onServiceSuspended(int cause) {
- Log.d(TAG, "Car Service connection suspended");
- }
-
- @Override
- public void onServiceConnectionFailed(int cause) {
- Log.d(TAG, "Car Service connection failed");
- }
- };
-
- private final class MyCarMenuCallbacks extends CarMenuCallbacks {
- /** Id for the root menu */
- private static final String ROOT = "ROOT";
- /** Id for the asynchronous menu */
- private static final String ASYNC = "ASYNC";
- /** Id for the checkbox menu itme */
- private static final String CHECKBOX = "checkbox";
-
- // for shared preferences
- /** Key used to store the checkbox state */
- private static final String KEY_CHECKED = "checked";
- /** Shared preferences name */
- private static final String PREFERENCES = "preferences";
-
- @Override
- public Root onGetRoot(Bundle hints) {
- return new Root(ROOT);
- }
-
- @Override
- public void onLoadChildren(String parentId, CarMenu result) {
- List<CarMenu.Item> items = new ArrayList<>();
- final Bitmap cheeseBitmap = BitmapFactory.decodeResource(getResources(),
- R.drawable.ic_cheese);
- // Demonstrate fetching the root menu.
- // It is not required to set all the fields.
- if (parentId.equals(ROOT)) {
- items.add(new CarMenu.Builder(ASYNC)
- .setTitle("Async")
- .setText("Send menu back asynchronously")
- .setFlags(CarMenu.FLAG_BROWSABLE)
- .build());
- items.add(new CarMenu.Builder(ROOT)
- .setTitle("Will be capped after 6 clicks")
- .setFlags(CarMenu.FLAG_BROWSABLE)
- .build());
-
- boolean checked = getContext()
- .getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE)
- .getBoolean(KEY_CHECKED, true);
- items.add(new CarMenu.Builder(CHECKBOX)
- .setTitle("Example checkbox")
- .setWidget(CarMenu.WIDGET_CHECKBOX)
- .setWidgetState(checked)
- .build());
- for (int i = 0; i < 26; ++i) {
- String id = String.valueOf((char)('A' + i));
- switch (i % 3) {
- case 0:
- // Set all the fields
- items.add(new CarMenu.Builder(id)
- .setTitle(String.valueOf(id))
- .setText("Cheeses that start with " + id)
- .setFlags(CarMenu.FLAG_BROWSABLE)
- .setIcon(cheeseBitmap)
- .build());
- break;
- case 1:
- // Only set title and text
- items.add(new CarMenu.Builder(id)
- .setTitle(String.valueOf(id))
- .setText("Cheeses that start with " + id)
- .setFlags(CarMenu.FLAG_BROWSABLE)
- .build());
- break;
- case 2:
- // Only set title
- items.add(new CarMenu.Builder(id)
- .setTitle(String.valueOf(id))
- .setFlags(CarMenu.FLAG_BROWSABLE)
- .build());
- break;
- }
- }
-
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- notifyChildChanged(ROOT, new CarMenu.Builder(ROOT)
- .setIcon(cheeseBitmap)
- .build());
- }
- }, 5000);
- } else if (parentId.equals(ASYNC)) {
- result.detach();
- sendAsync(result);
- return;
- } else {
- boolean started = false;
- for (String cheese : Cheeses.ITEMS) {
- if (cheese.startsWith(parentId)) {
- if (!started) {
- started = true;
- }
- items.add(new CarMenu.Builder(cheese)
- .setTitle(cheese)
- .setRightIcon(cheeseBitmap)
- .build());
- } else {
- if (started) {
- break;
- }
- }
- }
- }
- result.sendResult(items);
- }
-
- @Override
- public void onItemClicked(String id) {
- if (id.equals(CHECKBOX)) {
- boolean checked = getContext()
- .getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE)
- .getBoolean(KEY_CHECKED, true);
- getContext().getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).edit()
- .putBoolean(KEY_CHECKED, !checked)
- .commit();
- }
- mTextView1.setText(id);
- }
-
- @Override
- public void onCarMenuClosed() {
- }
-
- private void sendAsync(final CarMenu result) {
- final List<CarMenu.Item> items = new ArrayList<>();
- items.add(new CarMenu.Builder("Test")
- .setTitle("Appear after 5 sec")
- .build());
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- result.sendResult(items);
- }
- }, 5000);
- }
- }
-
-}
diff --git a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java b/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
index 9ff5511..3e05dd9 100644
--- a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
+++ b/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
@@ -27,7 +27,6 @@
import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
import com.android.car.vehiclenetwork.VehiclePropValueUtil;
-import com.android.support.car.test.MockedCarTestBase;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
@@ -120,7 +119,7 @@
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG |
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG |
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG |
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND |
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG |
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG,
v.getInt32Values(
VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_CONTEXTS)
@@ -149,7 +148,7 @@
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG |
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG |
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG |
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND |
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG |
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG,
v.getInt32Values(
VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_CONTEXTS)
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarAudioFocusTest.java b/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
similarity index 90%
rename from tests/carservice_test/src/com/android/support/car/test/CarAudioFocusTest.java
rename to tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
index 2f6e284..d3085f9 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarAudioFocusTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.support.car.test;
+package com.android.car.test;
import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
import android.content.Context;
@@ -24,6 +24,7 @@
import android.support.car.media.CarAudioManager;
import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioExtFocusFlag;
import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusIndex;
import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusRequest;
@@ -129,19 +130,10 @@
VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS,
VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE,
VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
- VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC3,
+ VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4,
VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
mAudioFocusPropertyHandler);
- getVehicleHalEmulator().addProperty(
- VehiclePropConfigUtil.getBuilder(
- VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_CONTEXT,
- VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE,
- VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
- VehicleValueType.VEHICLE_VALUE_TYPE_INT32,
- VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
- 0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
- mAppContextPropertyHandler);
getVehicleHalEmulator().addStaticProperty(
VehiclePropConfigUtil.createStaticStringProperty(
VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT),
@@ -155,35 +147,40 @@
checkSingleRequestRelease(
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN,
- VehicleAudioStream.VEHICLE_AUDIO_STREAM0);
+ VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
}
public void testMediaGainTransientFocus() throws Exception {
checkSingleRequestRelease(
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
- VehicleAudioStream.VEHICLE_AUDIO_STREAM0);
+ VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
}
public void testMediaGainTransientMayDuckFocus() throws Exception {
checkSingleRequestRelease(
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
- VehicleAudioStream.VEHICLE_AUDIO_STREAM0);
+ VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
}
public void testAlarmGainTransientFocus() throws Exception {
checkSingleRequestRelease(
AudioManager.STREAM_ALARM,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
- VehicleAudioStream.VEHICLE_AUDIO_STREAM1);
+ VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG);
}
public void testAlarmGainTransientMayDuckFocus() throws Exception {
checkSingleRequestRelease(
AudioManager.STREAM_ALARM,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
- VehicleAudioStream.VEHICLE_AUDIO_STREAM1);
+ VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG);
}
public void testMediaNavFocus() throws Exception {
@@ -197,6 +194,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
request[1],
@@ -214,6 +212,8 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
assertEquals(0x3, request[1]);
assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG |
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN, request[1],
VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
@@ -224,6 +224,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN, request[1],
VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
@@ -234,6 +235,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
assertEquals(0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS, request[1],
VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
@@ -250,6 +252,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
request[1],
@@ -275,6 +278,8 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK,
request[0]);
assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT,
0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
@@ -286,6 +291,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
assertEquals(0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
0,
@@ -302,6 +308,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
request[1],
@@ -313,6 +320,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
assertEquals(0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
0,
@@ -330,6 +338,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
request[1],
@@ -350,6 +359,8 @@
assertEquals(0, request[1]);
assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
request[2]);
+ // no android side context for radio
+ assertEquals(0, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
0,
@@ -369,6 +380,7 @@
assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
@@ -382,6 +394,7 @@
assertEquals(0, request[1]);
assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
request[2]);
+ assertEquals(0, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
0,
@@ -396,6 +409,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
@@ -407,13 +421,15 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
assertEquals(0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
0,
VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
}
- private void checkSingleRequestRelease(int streamType, int androidFocus, int streamNumber)
+ private void checkSingleRequestRelease(int streamType, int androidFocus, int streamNumber,
+ int context)
throws Exception {
AudioFocusListener lister = new AudioFocusListener();
int res = mAudioManager.requestAudioFocus(lister,
@@ -442,6 +458,7 @@
assertEquals(expectedRequest, request[0]);
assertEquals(0x1 << streamNumber, request[1]);
assertEquals(0, request[2]);
+ assertEquals(context, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
response,
request[1],
@@ -451,6 +468,7 @@
assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
assertEquals(0, request[1]);
assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
request[1],
@@ -491,6 +509,7 @@
private int mRequest;
private int mRequestedStreams;
private int mRequestedExtFocus;
+ private int mRequestedAudioContexts;
private final Semaphore mSetWaitSemaphore = new Semaphore(0);
@@ -500,7 +519,7 @@
mStreams = streams;
mExtFocus = extFocus;
}
- int[] values = { state, streams, extFocus };
+ int[] values = { state, streams, extFocus, 0 };
getVehicleHalEmulator().injectEvent(VehiclePropValueUtil.createIntVectorValue(
VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
SystemClock.elapsedRealtimeNanos()));
@@ -511,7 +530,8 @@
fail("timeout");
}
synchronized (this) {
- return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus };
+ return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus,
+ mRequestedAudioContexts };
}
}
@@ -525,6 +545,8 @@
VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_STREAMS);
mRequestedExtFocus = value.getInt32Values(
VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE);
+ mRequestedAudioContexts = value.getInt32Values(
+ VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS);
}
mSetWaitSemaphore.release();
}
@@ -538,7 +560,7 @@
streams = mStreams;
extFocus = mExtFocus;
}
- int[] values = { state, streams, extFocus };
+ int[] values = { state, streams, extFocus, 0 };
return VehiclePropValueUtil.createIntVectorValue(
VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
SystemClock.elapsedRealtimeNanos());
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarHvacManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/support/car/test/CarHvacManagerTest.java
rename to tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java
index 37fa0ec..13d0936 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarHvacManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.support.car.test;
+package com.android.car.test;
import android.util.Log;
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarPowerManagementTest.java b/tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/support/car/test/CarPowerManagementTest.java
rename to tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java
index 6cb3113..e0dc3d9 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarPowerManagementTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.support.car.test;
+package com.android.car.test;
import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
import android.content.Context;
diff --git a/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java
index ef7a371..4dcd3ce 100644
--- a/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java
@@ -31,7 +31,6 @@
import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
import com.android.car.vehiclenetwork.VehiclePropValueUtil;
-import com.android.support.car.test.MockedCarTestBase;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
diff --git a/tests/carservice_test/src/com/android/support/car/test/GarageModeTest.java b/tests/carservice_test/src/com/android/car/test/GarageModeTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/support/car/test/GarageModeTest.java
rename to tests/carservice_test/src/com/android/car/test/GarageModeTest.java
index bc6086d..8380ec2 100644
--- a/tests/carservice_test/src/com/android/support/car/test/GarageModeTest.java
+++ b/tests/carservice_test/src/com/android/car/test/GarageModeTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.support.car.test;
+package com.android.car.test;
import android.content.Context;
import android.test.AndroidTestCase;
diff --git a/tests/carservice_test/src/com/android/support/car/test/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
similarity index 98%
rename from tests/carservice_test/src/com/android/support/car/test/MockedCarTestBase.java
rename to tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
index 05d7104..5234dd5 100644
--- a/tests/carservice_test/src/com/android/support/car/test/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.support.car.test;
+package com.android.car.test;
import android.car.test.VehicleHalEmulator;
import android.content.ComponentName;
diff --git a/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java b/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java
index 76da972..18332da 100644
--- a/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java
+++ b/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java
@@ -22,6 +22,7 @@
import android.support.car.CarAppContextManager;
import android.util.Log;
+import com.android.car.test.MockedCarTestBase;
import com.android.car.vehiclenetwork.VehicleNetworkConsts;
import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
@@ -37,20 +38,10 @@
public class AppContextTest extends MockedCarTestBase {
private static final String TAG = AppContextTest.class.getSimpleName();
private static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
- private final AppContextHandler mAppContextHandler = new AppContextHandler();
@Override
protected void setUp() throws Exception {
super.setUp();
- getVehicleHalEmulator().addProperty(
- VehiclePropConfigUtil.getBuilder(
- VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_CONTEXT,
- VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE,
- VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
- VehicleValueType.VEHICLE_VALUE_TYPE_INT32,
- VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
- 0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
- mAppContextHandler);
getVehicleHalEmulator().start();
}
@@ -61,55 +52,12 @@
manager.registerContextListener(listener, CarAppContextManager.APP_CONTEXT_NAVIGATION |
CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
manager.setActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- mAppContextHandler.waitForPropertySetAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG);
manager.setActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- mAppContextHandler.waitForPropertySetAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG |
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG);
manager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- mAppContextHandler.waitForPropertySetAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG);
manager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- mAppContextHandler.waitForPropertySetAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0);
manager.unregisterContextListener();
}
- private class AppContextHandler implements VehicleHalPropertyHandler {
- private final Semaphore mWaitSemaphore = new Semaphore(0);
- private int mCurrentContext;
-
- public boolean waitForPropertySetAndAssert(long timeoutMs, int expected) throws Exception {
- if (!mWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expected, mCurrentContext);
- return true;
- }
-
- @Override
- public void onPropertySet(VehiclePropValue value) {
- mCurrentContext = value.getInt32Values(0);
- mWaitSemaphore.release();
- }
-
- @Override
- public VehiclePropValue onPropertyGet(VehiclePropValue value) {
- fail();
- return null;
- }
-
- @Override
- public void onPropertySubscribe(int property, float sampleRate, int zones) {
- fail();
- }
-
- @Override
- public void onPropertyUnsubscribe(int property) {
- fail();
- }
- }
-
private class ContextChangeListerner implements CarAppContextManager.AppContextChangeListener {
private int mLastChangeEvent;
private final Semaphore mChangeWait = new Semaphore(0);
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarConnectionListenerInMockingTest.java b/tests/carservice_test/src/com/android/support/car/test/CarConnectionListenerInMockingTest.java
index 710c309..0b80872 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarConnectionListenerInMockingTest.java
+++ b/tests/carservice_test/src/com/android/support/car/test/CarConnectionListenerInMockingTest.java
@@ -28,6 +28,8 @@
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import com.android.car.test.MockedCarTestBase;
+
public class CarConnectionListenerInMockingTest extends MockedCarTestBase {
private static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarInfoManagerTest.java b/tests/carservice_test/src/com/android/support/car/test/CarInfoManagerTest.java
index ae502d6..ada1d3a 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarInfoManagerTest.java
+++ b/tests/carservice_test/src/com/android/support/car/test/CarInfoManagerTest.java
@@ -19,6 +19,7 @@
import android.support.car.CarInfoManager;
import android.util.Log;
+import com.android.car.test.MockedCarTestBase;
import com.android.car.vehiclenetwork.VehicleNetworkConsts;
import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
import com.android.car.vehiclenetwork.VehiclePropValueUtil;
diff --git a/tests/carservice_test/src/com/android/support/car/test/CarPackageManagerTest.java b/tests/carservice_test/src/com/android/support/car/test/CarPackageManagerTest.java
index 9776bff..1abc49e 100644
--- a/tests/carservice_test/src/com/android/support/car/test/CarPackageManagerTest.java
+++ b/tests/carservice_test/src/com/android/support/car/test/CarPackageManagerTest.java
@@ -23,6 +23,7 @@
import android.content.pm.PackageManager;
import android.util.Log;
+import com.android.car.test.MockedCarTestBase;
import com.android.car.test.TestAppBlockingPolicyService;
public class CarPackageManagerTest extends MockedCarTestBase {
diff --git a/tests/libvehiclenetwork-native-test/AudioPropertyDef.c b/tests/libvehiclenetwork-native-test/AudioPropertyDef.c
index 28dbd23..0e00c7f 100644
--- a/tests/libvehiclenetwork-native-test/AudioPropertyDef.c
+++ b/tests/libvehiclenetwork-native-test/AudioPropertyDef.c
@@ -25,7 +25,7 @@
.prop = VEHICLE_PROPERTY_AUDIO_FOCUS,
.access = VEHICLE_PROP_ACCESS_READ_WRITE,
.change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
- .value_type = VEHICLE_VALUE_TYPE_INT32_VEC3,
+ .value_type = VEHICLE_VALUE_TYPE_INT32_VEC4,
.config_flags = 0x0,
.min_sample_rate = 0,
.max_sample_rate = 0,
diff --git a/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h b/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
index 196c556..47a3a28 100644
--- a/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
+++ b/tests/libvehiclenetwork-native-test/IVehicleNetworkTestListener.h
@@ -54,7 +54,7 @@
mHalErrorCondition.signal();
}
- virtual void onHalRestart(bool inMocking) {
+ virtual void onHalRestart(bool /*inMocking*/) {
Mutex::Autolock autolock(mHalRestartLock);
mHalRestartCount++;
mHalRestartCondition.signal();
diff --git a/tests/libvehiclenetwork-native-test/VehicleHalMock.h b/tests/libvehiclenetwork-native-test/VehicleHalMock.h
index 777bbbe..1d43a6d 100644
--- a/tests/libvehiclenetwork-native-test/VehicleHalMock.h
+++ b/tests/libvehiclenetwork-native-test/VehicleHalMock.h
@@ -42,19 +42,20 @@
return mProperties;
};
- virtual status_t onPropertySet(const vehicle_prop_value_t& value) {
+ virtual status_t onPropertySet(const vehicle_prop_value_t& /*value*/) {
return NO_ERROR;
};
- virtual status_t onPropertyGet(vehicle_prop_value_t* value) {
+ virtual status_t onPropertyGet(vehicle_prop_value_t* /*value*/) {
return NO_ERROR;
};
- virtual status_t onPropertySubscribe(int32_t property, float sampleRate, int32_t zones) {
+ virtual status_t onPropertySubscribe(int32_t /*property*/, float /*sampleRate*/,
+ int32_t /*zones*/) {
return NO_ERROR;
};
- virtual void onPropertyUnsubscribe(int32_t property) {
+ virtual void onPropertyUnsubscribe(int32_t /*property*/) {
};
bool isTheSameProperties(sp<VehiclePropertiesHolder>& list) {
diff --git a/tests/libvehiclenetwork-native-test/VehicleNetworkAudioHelperTest.cpp b/tests/libvehiclenetwork-native-test/VehicleNetworkAudioHelperTest.cpp
index 7dd6d73..af19b36 100644
--- a/tests/libvehiclenetwork-native-test/VehicleNetworkAudioHelperTest.cpp
+++ b/tests/libvehiclenetwork-native-test/VehicleNetworkAudioHelperTest.cpp
@@ -49,10 +49,11 @@
mAudioProperties->getList().push_back(properties + i);
}
mValueToGet.prop = VEHICLE_PROPERTY_AUDIO_FOCUS;
- mValueToGet.value_type = VEHICLE_VALUE_TYPE_INT32_VEC3;
+ mValueToGet.value_type = VEHICLE_VALUE_TYPE_INT32_VEC4;
mValueToGet.value.int32_array[0] = 0;
mValueToGet.value.int32_array[1] = 0;
mValueToGet.value.int32_array[2] = 0;
+ mValueToGet.value.int32_array[3] = 0;
}
virtual ~VehicleHalMockForAudioFocus() {};
@@ -76,7 +77,8 @@
return NO_ERROR;
};
- virtual status_t onPropertySubscribe(int32_t property, float sampleRate) {
+ virtual status_t onPropertySubscribe(int32_t property, float /*sampleRate*/,
+ int32_t /*zones*/) {
ALOGI("onPropertySubscribe 0x%x", property);
return NO_ERROR;
};
@@ -90,6 +92,7 @@
mValueToGet.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_FOCUS] = state;
mValueToGet.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_STREAMS] = streams;
mValueToGet.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE] = extState;
+ mValueToGet.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS] = 0;
mValueToGet.timestamp = elapsedRealtimeNano();
mVN->injectEvent(mValueToGet);
}
diff --git a/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h b/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
index 1260876..e661eff 100644
--- a/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
+++ b/tests/libvehiclenetwork-native-test/VehicleNetworkTestListener.h
@@ -61,11 +61,11 @@
mCondition.signal();
}
- virtual void onHalError(int32_t errorCode, int32_t property, int32_t operation) {
+ virtual void onHalError(int32_t /*errorCode*/, int32_t /*property*/, int32_t /*operation*/) {
//TODO
}
- virtual void onHalRestart(bool inMocking) {
+ virtual void onHalRestart(bool /*inMocking*/) {
//TODO cannot test this in native world without plumbing mocking
}
diff --git a/vehicle_network_service/VehicleHalPropertyUtil.h b/vehicle_network_service/VehicleHalPropertyUtil.h
index 3b22797..da0d476 100644
--- a/vehicle_network_service/VehicleHalPropertyUtil.h
+++ b/vehicle_network_service/VehicleHalPropertyUtil.h
@@ -48,7 +48,7 @@
config.float_max_value);
} break;
case VEHICLE_VALUE_TYPE_INT64: {
- msg.appendFormat(",v min:" PRId64 " max:" PRId64 "\n", config.int64_min_value,
+ msg.appendFormat(",v min:%" PRId64 " max:%" PRId64 "\n", config.int64_min_value,
config.int64_max_value);
} break;
case VEHICLE_VALUE_TYPE_INT32:
diff --git a/vehicle_network_service/VehicleNetworkService.cpp b/vehicle_network_service/VehicleNetworkService.cpp
index e3b76c0..278b0cd 100644
--- a/vehicle_network_service/VehicleNetworkService.cpp
+++ b/vehicle_network_service/VehicleNetworkService.cpp
@@ -779,8 +779,11 @@
do {
Mutex::Autolock autoLock(mLock);
if (mMockingEnabled) {
- ALOGE("startMocking while already enabled");
- return INVALID_OPERATION;
+ ALOGW("startMocking while already enabled");
+ // allow it as test can fail without clearing
+ if (mHalMock != NULL) {
+ IInterface::asBinder(mHalMock)->unlinkToDeath(mHalMockDeathHandler.get());
+ }
}
ALOGW("starting vehicle HAL mocking");
sp<IBinder> ibinder = IInterface::asBinder(mock);