Add fragment for BluetoothConnection management
Currently handles only HFP based connection properties
Change-Id: Id75f3e4d6805318a5a025dcfd3eee5b2045dfd81
diff --git a/tests/EmbeddedKitchenSinkApp/Android.mk b/tests/EmbeddedKitchenSinkApp/Android.mk
index 49d578a..ca32045 100644
--- a/tests/EmbeddedKitchenSinkApp/Android.mk
+++ b/tests/EmbeddedKitchenSinkApp/Android.mk
@@ -30,6 +30,8 @@
LOCAL_PRIVILEGED_MODULE := true
+LOCAL_CERTIFICATE := platform
+
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index f83a923..95eb5f8 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -16,7 +16,8 @@
<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" >
+ package="com.google.android.car.kitchensink"
+ android:sharedUserId="android.uid.system">
<uses-sdk
android:minSdkVersion="22"
android:targetSdkVersion='23'/>
@@ -38,6 +39,7 @@
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application android:label="@string/app_title"
android:icon="@drawable/ic_launcher">
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml b/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
new file mode 100644
index 0000000..3ef75e2
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.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">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
+ <TextView
+ android:id="@+id/bluetooth_device"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+ <Button
+ android:id="@+id/bluetooth_pick_device"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bluetooth_pick_device"/>
+ </LinearLayout>
+ <Button
+ android:id="@+id/bluetooth_sco_connect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bluetooth_sco_connect"/>
+ <Button
+ android:id="@+id/bluetooth_sco_disconnect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bluetooth_sco_disconnect"/>
+ <Button
+ android:id="@+id/bluetooth_quiet_mode_enable"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bluetooth_quiet_mode_enable"/>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index a6a9e69..a3f45a0 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -195,6 +195,12 @@
<string name="sms_reply_busy" translatable="false">Reply "I'm Busy"</string>
<string name="sms_reply_sent" translatable="false">Reply Sent</string>
<string name="sms_reply_delivered" translatable="false">Reply Delivered</string>
+ <string name="bluetooth_profile_connect">Profile connect</string>
+ <string name="bluetooth_profile_disconnect">Profile disconnect</string>
+ <string name="bluetooth_sco_connect">SCO connect</string>
+ <string name="bluetooth_sco_disconnect">SCO disconnect</string>
+ <string name="bluetooth_pick_device">Pick Device</string>
+ <string name="bluetooth_quiet_mode_enable">Quiet Mode</string>
<!--Car Service Settings-->
<string name="garage_title">Garage Mode</string>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index f31cd26..527b09b 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -50,7 +50,8 @@
import com.google.android.car.kitchensink.setting.CarServiceSettingsActivity;
import com.google.android.car.kitchensink.touch.TouchTestFragment;
import com.google.android.car.kitchensink.volume.VolumeTestFragment;
-//import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
+import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
+import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment;
import java.util.ArrayList;
import java.util.List;
@@ -73,6 +74,7 @@
private static final String MENU_CUBES_TEST = "cubes test";
private static final String MENU_CAR_SETTINGS = "car service settings";
private static final String MENU_ORIENTATION = "orientation test";
+ private static final String MENU_BLUETOOTH_HEADSET = "bluetooth headset";
private static final String MENU_MAP_MESSAGING = "bluetooth messaging test";
private Car mCarApi;
@@ -94,7 +96,8 @@
private TouchTestFragment mTouchTestFragment;
private CubesTestFragment mCubesTestFragment;
private OrientationTestFragment mOrientationFragment;
- //private MapMceTestFragment mMapMceTestFragment;
+ private MapMceTestFragment mMapMceTestFragment;
+ private BluetoothHeadsetFragment mBluetoothHeadsetFragement;
private final CarSensorManager.OnSensorChangedListener mListener =
new CarSensorManager.OnSensorChangedListener() {
@@ -224,7 +227,7 @@
MENU_AUDIO, MENU_RADIO, MENU_CAMERA, MENU_HVAC, MENU_JOB, MENU_KEYBOARD,
MENU_CLUSTER, MENU_INPUT_TEST, MENU_SENSORS, MENU_VOLUME_TEST,
MENU_TOUCH_TEST, MENU_CUBES_TEST, MENU_CAR_SETTINGS, MENU_ORIENTATION,
- MENU_MAP_MESSAGING, MENU_QUIT
+ MENU_BLUETOOTH_HEADSET, MENU_MAP_MESSAGING, MENU_QUIT
};
for (String menu : allMenus) {
items.add(new CarMenu.Builder(menu).setText(menu).build());
@@ -312,12 +315,17 @@
mOrientationFragment = new OrientationTestFragment();
}
showFragment(mOrientationFragment);
- //} else if (id.equals(MENU_MAP_MESSAGING)) {
- // if (mMapMceTestFragment == null) {
- // mMapMceTestFragment = new MapMceTestFragment();
- // }
- // showFragment(mMapMceTestFragment);
- } else if (id.equals(MENU_QUIT)) {
+ } else if (id.equals(MENU_BLUETOOTH_HEADSET)) {
+ if (mBluetoothHeadsetFragement == null) {
+ mBluetoothHeadsetFragement = new BluetoothHeadsetFragment();
+ }
+ showFragment(mBluetoothHeadsetFragement);
+ } else if (id.equals(MENU_MAP_MESSAGING)) {
+ if (mMapMceTestFragment == null) {
+ mMapMceTestFragment = new MapMceTestFragment();
+ }
+ showFragment(mMapMceTestFragment);
+ }else if (id.equals(MENU_QUIT)) {
finish();
}
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
new file mode 100644
index 0000000..4965113
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
@@ -0,0 +1,205 @@
+/*
+ * 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.bluetooth;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDevicePicker;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+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.RadioGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.List;
+
+public class BluetoothHeadsetFragment extends Fragment {
+ private static final String TAG = "CAR.BLUETOOTH.KS";
+ BluetoothAdapter mBluetoothAdapter;
+ BluetoothDevice mPickedDevice;
+
+ TextView mPickedDeviceText;
+ Button mDevicePicker;
+ Button mScoConnect;
+ Button mScoDisconnect;
+ Button mEnableQuietMode;
+
+ BluetoothHeadsetClient mHfpClientProfile;
+
+ // Intent for picking a Bluetooth device
+ public static final String DEVICE_PICKER_ACTION = "android.bluetooth.devicepicker.action.LAUNCH";
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.bluetooth_headset, container, false);
+
+ mPickedDeviceText = (TextView) v.findViewById(R.id.bluetooth_device);
+ mDevicePicker = (Button) v.findViewById(R.id.bluetooth_pick_device);
+ mScoConnect = (Button) v.findViewById(R.id.bluetooth_sco_connect);
+ mScoDisconnect= (Button) v.findViewById(R.id.bluetooth_sco_disconnect);
+ mEnableQuietMode = (Button) v.findViewById(R.id.bluetooth_quiet_mode_enable);
+
+ // Pick a bluetooth device
+ mDevicePicker.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ launchDevicePicker();
+ }
+ });
+
+ // Connect SCO
+ mScoConnect.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ connectSco();
+ }
+ });
+
+ // Disconnect SCO
+ mScoDisconnect.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ disconnectSco();
+ }
+ });
+
+ // Enable quiet mode
+ mEnableQuietMode.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mBluetoothAdapter.enableNoAutoConnect();
+ }
+ });
+ return v;
+ }
+
+ void launchDevicePicker() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
+ getContext().registerReceiver(mPickerReceiver, filter);
+
+ Intent intent = new Intent(DEVICE_PICKER_ACTION);
+ intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ getContext().startActivity(intent);
+ }
+
+ void connectSco() {
+ if (mPickedDevice == null) {
+ Log.w(TAG, "Device null when trying to connect sco!");
+ return;
+ }
+
+ // Check if we have the proxy and connect the device.
+ if (mHfpClientProfile == null) {
+ Log.w(TAG, "HFP Profile proxy not available, cannot connect sco to " + mPickedDevice);
+ return;
+ }
+ mHfpClientProfile.connectAudio(mPickedDevice);
+ }
+
+ void disconnectSco() {
+ if (mPickedDevice == null) {
+ Log.w(TAG, "Device null when trying to disconnect sco!");
+ return;
+ }
+
+ if (mHfpClientProfile == null) {
+ Log.w(TAG, "HFP Profile proxy not available, cannot disconnect sco to " +
+ mPickedDevice);
+ return;
+ }
+ mHfpClientProfile.disconnectAudio(mPickedDevice);
+ }
+
+ private final BroadcastReceiver mPickerReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ Log.v(TAG, "mPickerReceiver got " + action);
+
+ if (BluetoothDevicePicker.ACTION_DEVICE_SELECTED.equals(action)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ mPickedDevice = device;
+ String text = device.getName() == null ?
+ device.getAddress() : device.getName() + " " + device.getAddress();
+ mPickedDeviceText.setText(text);
+
+ // The receiver can now be disabled.
+ getContext().unregisterReceiver(mPickerReceiver);
+ }
+ }
+ };
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mBluetoothAdapter.getProfileProxy(
+ getContext(), new ProfileServiceListener(), BluetoothProfile.HEADSET_CLIENT);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ class ProfileServiceListener implements BluetoothProfile.ServiceListener {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ Log.d(TAG, "Proxy connected for profile: " + profile);
+ switch (profile) {
+ case BluetoothProfile.HEADSET_CLIENT:
+ mHfpClientProfile = (BluetoothHeadsetClient) proxy;
+ break;
+ default:
+ Log.w(TAG, "onServiceConnected not supported profile: " + profile);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ Log.d(TAG, "Proxy disconnected for profile: " + profile);
+ switch (profile) {
+ case BluetoothProfile.HEADSET_CLIENT:
+ mHfpClientProfile = null;
+ break;
+ default:
+ Log.w(TAG, "onServiceDisconnected not supported profile: " + profile);
+ }
+ }
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapMceTestFragment.java.disabled b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapMceTestFragment.java
similarity index 100%
rename from tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapMceTestFragment.java.disabled
rename to tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapMceTestFragment.java
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java.disabled b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java
similarity index 100%
rename from tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java.disabled
rename to tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java