Merge "Bluetooth: Test App for MAP, PBAP and HFP client role."
diff --git a/Android.mk b/Android.mk
index 4e4a62c..bbd17ec 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,34 +23,5 @@
include $(BUILD_PACKAGE)
-include $(CLEAR_VARS)
-
-src_dirs:= src/org/codeaurora/bluetooth/mapclient
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, $(src_dirs))
-
-LOCAL_MODULE:= org.codeaurora.bluetooth.mapclient
-LOCAL_JAVA_LIBRARIES := javax.obex
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-
-src_dirs:= src/org/codeaurora/bluetooth/pbapclient
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, $(src_dirs))
-
-LOCAL_MODULE:= org.codeaurora.bluetooth.pbapclient
-LOCAL_JAVA_LIBRARIES := javax.obex
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/bttestapp/Android.mk b/bttestapp/Android.mk
new file mode 100644
index 0000000..f560163
--- /dev/null
+++ b/bttestapp/Android.mk
@@ -0,0 +1,50 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := BTTestApp
+LOCAL_CERTIFICATE := platform
+
+LOCAL_MODULE_OWNER := qcom
+
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard org.codeaurora.bluetooth.mapclient org.codeaurora.bluetooth.pbapclient
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+
+src_dirs:= ../src/org/codeaurora/bluetooth/mapclient
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, $(src_dirs))
+
+LOCAL_MODULE:= org.codeaurora.bluetooth.mapclient
+LOCAL_JAVA_LIBRARIES := javax.obex
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+
+src_dirs:= ../src/org/codeaurora/bluetooth/pbapclient
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, $(src_dirs))
+
+LOCAL_MODULE:= org.codeaurora.bluetooth.pbapclient
+LOCAL_JAVA_LIBRARIES := javax.obex
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/bttestapp/AndroidManifest.xml b/bttestapp/AndroidManifest.xml
new file mode 100644
index 0000000..f96e4a5
--- /dev/null
+++ b/bttestapp/AndroidManifest.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.codeaurora.bluetooth.bttestapp"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_STACK" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+ <uses-sdk
+ android:minSdkVersion="17"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+
+ <uses-library android:name="javax.obex" />
+
+ <activity android:name=".MainActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".VcardFilterActivity" />
+ <!--activity
+ android:name=".HfpTestActivity"
+ android:launchMode="singleTop" /-->
+ <activity
+ android:name=".PbapTestActivity"
+ android:launchMode="singleTop" />
+ <activity
+ android:name=".MapTestActivity"
+ android:launchMode="singleTop" />
+ <activity android:name=".MessageFilterActivity" />
+
+ <service android:name=".ProfileService" />
+<!--
+ <service android:name=".mapclient.BluetoothMnsService">
+ <intent-filter>
+ <action android:name="org.codeaurora.bluetooth.mapclient.BluetoothMnsService" />
+ </intent-filter>
+ </service>
+-->
+ <activity android:name=".services.PbapAuthActivity"
+ android:excludeFromRecents="true"
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert">
+ <intent-filter>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <receiver android:name=".BluetoothConnectionReceiver" >
+ <intent-filter>
+ <action android:name="org.codeaurora.bluetooth.action.NEW_BLUETOOTH_DEVICE" />
+ <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
+ </intent-filter>
+ </receiver>
+
+ </application>
+
+</manifest>
diff --git a/bttestapp/res/drawable-hdpi/ic_bt_connected.png b/bttestapp/res/drawable-hdpi/ic_bt_connected.png
new file mode 100644
index 0000000..94c0ffc
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_bt_connected.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_bt_disconnected.png b/bttestapp/res/drawable-hdpi/ic_bt_disconnected.png
new file mode 100644
index 0000000..e7689b3
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_bt_disconnected.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_call_incoming_holo_dark.png b/bttestapp/res/drawable-hdpi/ic_call_incoming_holo_dark.png
new file mode 100644
index 0000000..aaa28d0
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_call_incoming_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_call_outgoing_holo_dark.png b/bttestapp/res/drawable-hdpi/ic_call_outgoing_holo_dark.png
new file mode 100644
index 0000000..f52677a
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_call_outgoing_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_contact_picture.png b/bttestapp/res/drawable-hdpi/ic_contact_picture.png
new file mode 100644
index 0000000..0997e31
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_launcher.png b/bttestapp/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..f83649f
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/ic_menu_save.png b/bttestapp/res/drawable-hdpi/ic_menu_save.png
new file mode 100644
index 0000000..306d55a
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/ic_menu_save.png
Binary files differ
diff --git a/bttestapp/res/drawable-hdpi/sym_keyboard_delete.png b/bttestapp/res/drawable-hdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..77cdf75
--- /dev/null
+++ b/bttestapp/res/drawable-hdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_bt_connected.png b/bttestapp/res/drawable-mdpi/ic_bt_connected.png
new file mode 100644
index 0000000..da98b1b
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_bt_connected.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_bt_disconnected.png b/bttestapp/res/drawable-mdpi/ic_bt_disconnected.png
new file mode 100644
index 0000000..c8eeeee
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_bt_disconnected.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_call_incoming_holo_dark.png b/bttestapp/res/drawable-mdpi/ic_call_incoming_holo_dark.png
new file mode 100644
index 0000000..98678c9
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_call_incoming_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_call_outgoing_holo_dark.png b/bttestapp/res/drawable-mdpi/ic_call_outgoing_holo_dark.png
new file mode 100644
index 0000000..bbe177c
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_call_outgoing_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_contact_picture.png b/bttestapp/res/drawable-mdpi/ic_contact_picture.png
new file mode 100644
index 0000000..b1f8be1
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_launcher.png b/bttestapp/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..8c4edba
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/ic_menu_save.png b/bttestapp/res/drawable-mdpi/ic_menu_save.png
new file mode 100644
index 0000000..ad07695
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/ic_menu_save.png
Binary files differ
diff --git a/bttestapp/res/drawable-mdpi/sym_keyboard_delete.png b/bttestapp/res/drawable-mdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..761e7ba
--- /dev/null
+++ b/bttestapp/res/drawable-mdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_bt_connected.png b/bttestapp/res/drawable-xhdpi/ic_bt_connected.png
new file mode 100644
index 0000000..22f1f5f
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_bt_connected.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_bt_disconnected.png b/bttestapp/res/drawable-xhdpi/ic_bt_disconnected.png
new file mode 100644
index 0000000..569f2ba
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_bt_disconnected.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_call_incoming_holo_dark.png b/bttestapp/res/drawable-xhdpi/ic_call_incoming_holo_dark.png
new file mode 100644
index 0000000..d787229
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_call_incoming_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_call_outgoing_holo_dark.png b/bttestapp/res/drawable-xhdpi/ic_call_outgoing_holo_dark.png
new file mode 100644
index 0000000..ada5cf8
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_call_outgoing_holo_dark.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_contact_picture.png b/bttestapp/res/drawable-xhdpi/ic_contact_picture.png
new file mode 100644
index 0000000..46ef148
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_contact_picture.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_launcher.png b/bttestapp/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..b4d5d6c
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/ic_menu_save.png b/bttestapp/res/drawable-xhdpi/ic_menu_save.png
new file mode 100644
index 0000000..2720602
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/ic_menu_save.png
Binary files differ
diff --git a/bttestapp/res/drawable-xhdpi/sym_keyboard_delete.png b/bttestapp/res/drawable-xhdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..384315e
--- /dev/null
+++ b/bttestapp/res/drawable-xhdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/bttestapp/res/drawable/selected_call_bg.xml b/bttestapp/res/drawable/selected_call_bg.xml
new file mode 100644
index 0000000..bd0d805
--- /dev/null
+++ b/bttestapp/res/drawable/selected_call_bg.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <solid android:color="@color/call_selected" />
+ <stroke android:width="3dip" android:color="#800000" />
+
+</shape>
diff --git a/bttestapp/res/layout/activity_hfp_test.xml b/bttestapp/res/layout/activity_hfp_test.xml
new file mode 100644
index 0000000..5c3071a
--- /dev/null
+++ b/bttestapp/res/layout/activity_hfp_test.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:layout_margin="5dip"
+ android:baselineAligned="false">
+
+ <LinearLayout
+ android:id="@+id/hfptest_left_panel"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="35"
+ android:orientation="vertical"
+ android:paddingRight="10dip">
+
+ <fragment class="org.codeaurora.bluetooth.bttestapp.IndicatorsFragment"
+ android:id="@+id/indicators"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <fragment class="org.codeaurora.bluetooth.bttestapp.DialpadFragment"
+ android:id="@+id/dialpad"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <fragment class="org.codeaurora.bluetooth.bttestapp.CallsListFragment"
+ android:id="@+id/calls_list"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="65" />
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/activity_main.xml b/bttestapp/res/layout/activity_main.xml
new file mode 100644
index 0000000..edfdd60
--- /dev/null
+++ b/bttestapp/res/layout/activity_main.xml
@@ -0,0 +1,107 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<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:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity" >
+
+ <RelativeLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp" >
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_alignParentLeft="true" >
+
+ <TextView
+ android:id="@+id/device_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/blank"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/device_address"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/blank"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:layout_alignParentRight="true" >
+
+ <Button
+ android:id="@+id/discover_services"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/discover_services"
+ android:onClick="onButtonClick" />
+
+ <Button
+ android:id="@+id/select_device"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/select_device"
+ android:onClick="onButtonClick" />
+
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/services_header"
+ style="?android:attr/listSeparatorTextViewStyle" />
+
+ <fragment class="org.codeaurora.bluetooth.bttestapp.ServicesFragment"
+ android:id="@+id/services_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="16dp" />
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/activity_map_test.xml b/bttestapp/res/layout/activity_map_test.xml
new file mode 100644
index 0000000..351a860
--- /dev/null
+++ b/bttestapp/res/layout/activity_map_test.xml
@@ -0,0 +1,682 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="#101010" >
+
+<LinearLayout
+ android:id="@+id/maptest_folder_nav"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#003000"
+ style="@style/somePadding" >
+
+ <Button
+ android:id="@+id/maptest_nav_root"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/map_folder_root"
+ android:onClick="onClickSetPathRoot" />
+
+ <Button
+ android:id="@+id/maptest_nav_up"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/map_folder_up"
+ android:onClick="onClickSetPathUp" />
+
+ <TextView
+ android:id="@+id/maptest_nav_current"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginLeft="20dp"
+ android:layout_marginRight="10dp"
+ android:gravity="center_vertical|right"
+ android:text="@string/blank" />
+
+ <Spinner
+ android:id="@+id/maptest_nav_folders"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/maptest_nav_down"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/map_folder_enter"
+ android:onClick="onClickSetPathEnter" />
+
+ <EditText
+ android:layout_marginLeft="40dp"
+ android:id="@+id/maptest_nav_list_max"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:ems="2"
+ android:inputType="number" />
+
+ <EditText
+ android:id="@+id/maptest_nav_list_offset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:ems="2"
+ android:inputType="number" />
+
+ <Button
+ android:id="@+id/maptest_nav_list_get"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/map_folder_listing"
+ android:onClick="onClickGetFolderListing" />
+
+ <Button
+ android:id="@+id/maptest_nav_list_size"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/map_folder_listing_size"
+ android:onClick="onClickGetFolderListingSize" />
+
+</LinearLayout>
+
+<ViewFlipper
+ android:id="@+id/maptest_viewflipper"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+<!-- LIST TAB -->
+<LinearLayout
+ android:id="@+id/maptest_tab_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal" >
+
+ <!-- Browsing Layout -->
+ <LinearLayout
+ android:layout_width="250dp"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="#000030" >
+
+ <!-- Horizontal line -->
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <Button
+ android:id="@+id/maptest_msglist_params"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/map_message_parameters"
+ android:onClick="onClickMessageParameters" />
+
+ <Button
+ android:id="@+id/maptest_msglist_filter"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/map_message_filter"
+ android:onClick="onClickGetMessagesFilter" />
+
+ </LinearLayout>
+
+ <TableLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusableInTouchMode="true" >
+
+ <!-- MaxListCount -->
+ <TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_max_list_count_prompt" />
+
+ <EditText
+ android:id="@+id/maptest_msglist_max"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="number" />
+ </TableRow>
+
+ <!-- ListStartOffset -->
+ <TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_list_start_offset_prompt" />
+
+ <EditText
+ android:id="@+id/maptest_msglist_offset"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="number" />
+ </TableRow>
+
+ <!-- SubjectLength -->
+ <TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_subject_length_prompt" />
+
+ <EditText
+ android:id="@+id/maptest_msglist_subject_len"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="number" />
+ </TableRow>
+
+ </TableLayout>
+
+ <Button
+ android:id="@+id/maptest_msglist_get"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/map_message_listing"
+ android:onClick="onClickGetMessagesListing" />
+
+ <Button
+ android:id="@+id/maptest_msglist_get_size"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/map_message_listing_size"
+ android:onClick="onClickGetMessageListingSize" />
+
+ </LinearLayout>
+
+ <!-- Messages layout -->
+ <ListView
+ android:id="@+id/maptest_msglist_lv"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <ProgressBar
+ android:id="@+id/maptest_msglist_progressbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/progressBarStyleLarge"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/maptest_msglist_empty"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_messages_list_empty"
+ android:layout_gravity="center_horizontal|center_vertical"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</LinearLayout>
+
+<!-- PREVIEW TAB -->
+<LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#000030"
+ style="@style/somePadding" >
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:orientation="horizontal" >
+ <Button
+ android:id="@+id/map_msg_get"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_get_lbl"
+ android:onClick="onClickGetMessage" />
+ <EditText
+ android:id="@+id/map_msg_handle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10" />
+ <RadioGroup
+ android:id="@+id/map_msg_get_charset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="horizontal" >
+ <RadioButton
+ android:id="@+id/map_msg_get_charset_native"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:text="@string/charset_native_lbl" />
+ <RadioButton
+ android:id="@+id/map_msg_get_charset_utf8"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:text="@string/charset_utf8_lbl"
+ android:checked="true" />
+ </RadioGroup>
+ <CheckBox
+ android:id="@+id/map_msg_get_attachment"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_get_attachment_lbl" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/map_msg_copy_to_editor"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:text="@string/map_msg_copy_to_editor_lbl"
+ android:onClick="onClickCopyMessage" />
+ <Button
+ android:id="@+id/map_msg_delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_delete_message"
+ android:layout_toLeftOf="@id/map_msg_copy_to_editor"
+ android:onClick="onClickDeleteMessage" />
+ <Button
+ android:id="@+id/map_msg_set_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_set_unread"
+ android:layout_toLeftOf="@id/map_msg_delete"
+ android:onClick="onClickSetStatus" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ style="@style/somePadding" >
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_bmsg_status_prompt" />
+ <EditText
+ android:id="@+id/maptest_bmsg_status"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_bmsg_type_prompt" />
+ <EditText
+ android:id="@+id/maptest_bmsg_type"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="3">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_bmsg_folder_prompt" />
+ <EditText
+ android:id="@+id/maptest_bmsg_folder"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ style="@style/somePadding" >
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_bbody_encoding_prompt" />
+ <EditText
+ android:id="@+id/maptest_bbody_encoding"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_bbody_charset_prompt" />
+ <EditText
+ android:id="@+id/maptest_bbody_charset"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_bbody_language_prompt" />
+ <EditText
+ android:id="@+id/maptest_bbody_language"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="#303030"
+ style="@style/somePadding" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_originators_prompt" />
+ <EditText
+ android:id="@+id/maptest_orig"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="#303030"
+ style="@style/somePadding" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_recipients_prompt" />
+ <EditText
+ android:id="@+id/maptest_rcpt"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <EditText
+ android:id="@+id/maptest_message"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:typeface="monospace"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ style="@style/somePadding" />
+
+</LinearLayout>
+
+
+<LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="#000030"
+ style="@style/somePadding" >
+
+ <Button
+ android:id="@+id/map_msg_push"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_push_lbl"
+ android:onClick="onClickPushMessage" />
+ <CheckBox
+ android:id="@+id/map_msg_push_transparent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_push_transparent_lbl" />
+ <CheckBox
+ android:id="@+id/map_msg_push_retry"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_push_retry_lbl" />
+ <RadioGroup
+ android:id="@+id/map_msg_push_charset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="horizontal" >
+ <RadioButton
+ android:id="@+id/map_msg_push_charset_native"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:text="@string/charset_native_lbl" />
+ <RadioButton
+ android:id="@+id/map_msg_push_charset_utf8"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:text="@string/charset_utf8_lbl"
+ android:checked="true" />
+ </RadioGroup>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ style="@style/somePadding" >
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_bmsg_type_prompt" />
+ <Spinner
+ android:id="@+id/bmsgedit_type"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:entries="@array/map_message_type_array" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_bbody_encoding_prompt" />
+ <Spinner
+ android:id="@+id/bmsgedit_encoding"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:entries="@array/map_message_encoding_array" />
+ </LinearLayout>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="3" />
+
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="#303030"
+ style="@style/somePadding" >
+
+ <TextView
+ android:id="@+id/bmsgedit_orig_add_lbl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:text="@string/map_originators_prompt" />
+ <Button
+ android:id="@+id/bmsgedit_orig_del"
+ android:text="@string/ppl_del"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:ems="1"
+ android:onClick="onClickRemovePeople" />
+ <Button
+ android:id="@+id/bmsgedit_orig_add"
+ android:text="@string/ppl_add"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@id/bmsgedit_orig_del"
+ android:ems="1"
+ android:onClick="onClickAddPerson" />
+ <EditText
+ android:id="@+id/bmsgedit_orig"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@id/bmsgedit_orig_add"
+ android:layout_toRightOf="@id/bmsgedit_orig_add_lbl" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="#303030"
+ style="@style/somePadding" >
+
+ <TextView
+ android:id="@+id/bmsgedit_rcpt_add_lbl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:text="@string/map_recipients_prompt" />
+ <Button
+ android:id="@+id/bmsgedit_rcpt_del"
+ android:text="@string/ppl_del"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:ems="1"
+ android:onClick="onClickRemovePeople" />
+ <Button
+ android:id="@+id/bmsgedit_rcpt_add"
+ android:text="@string/ppl_add"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@id/bmsgedit_rcpt_del"
+ android:ems="1"
+ android:onClick="onClickAddPerson" />
+ <EditText
+ android:id="@+id/bmsgedit_rcpt"
+ android:editable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@id/bmsgedit_rcpt_add"
+ android:layout_toRightOf="@id/bmsgedit_rcpt_add_lbl" />
+
+ </RelativeLayout>
+
+ <EditText
+ android:id="@+id/bmsgedit_contents"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:typeface="monospace"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ style="@style/somePadding" />
+
+</LinearLayout>
+
+</ViewFlipper>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/activity_message_filter.xml b/bttestapp/res/layout/activity_message_filter.xml
new file mode 100644
index 0000000..6f0334c
--- /dev/null
+++ b/bttestapp/res/layout/activity_message_filter.xml
@@ -0,0 +1,147 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="horizontal" >
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <CheckBox
+ android:id="@+id/param_subject"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_subject" />
+
+ <CheckBox
+ android:id="@+id/param_datatime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_datetime" />
+
+ <CheckBox
+ android:id="@+id/param_sender_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_sender_name" />
+
+ <CheckBox
+ android:id="@+id/param_sender_addressing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_sender_addressing" />
+
+ <CheckBox
+ android:id="@+id/param_receipent_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_recipient_name" />
+
+ <CheckBox
+ android:id="@+id/param_receipent_addressing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_recipient_addressing" />
+
+ <CheckBox
+ android:id="@+id/param_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_type" />
+
+ <CheckBox
+ android:id="@+id/param_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_size" />
+
+ <CheckBox
+ android:id="@+id/param_reception_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_reception_status" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <CheckBox
+ android:id="@+id/param_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_text" />
+
+ <CheckBox
+ android:id="@+id/param_attachment_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_attachment_size" />
+
+ <CheckBox
+ android:id="@+id/param_priority"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_priority" />
+
+ <CheckBox
+ android:id="@+id/param_read"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_read" />
+
+ <CheckBox
+ android:id="@+id/param_sent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_sent" />
+
+ <CheckBox
+ android:id="@+id/param_protected"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_protected" />
+
+ <CheckBox
+ android:id="@+id/param_replayto_addressing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_parameter_replyto_addressing" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/activity_pbap_test.xml b/bttestapp/res/layout/activity_pbap_test.xml
new file mode 100644
index 0000000..87561ec
--- /dev/null
+++ b/bttestapp/res/layout/activity_pbap_test.xml
@@ -0,0 +1,561 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pbap_test_viewflipper"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+<RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="horizontal" >
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/label_download_phonebook_prompt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:text="@string/phonebook_prompt" />
+
+ <Spinner
+ android:id="@+id/pbap_download_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:entries="@array/phonebook_arrays"
+ android:prompt="@string/phonebook_prompt"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/label_download_phonebook_prompt"/>
+
+ <TextView
+ android:id="@+id/label_download_format_prompt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/pbap_download_spinner"
+ android:layout_marginTop="5dp"
+ android:text="@string/vcard_format_prompt" />
+
+ <RadioGroup
+ android:id="@+id/pbap_download_formats"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/label_download_format_prompt"
+ android:orientation="horizontal" >
+
+ <RadioButton
+ android:id="@+id/pbap_download_vcard21"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/vcard21"
+ android:checked="true" />
+
+ <RadioButton
+ android:id="@+id/pbap_download_vcard30"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/vcard30" />
+ </RadioGroup>
+
+ <LinearLayout
+ android:id="@+id/pbap_layout_1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/pbap_download_formats"
+ android:layout_marginTop="5dp" >
+ <TextView
+ android:id="@+id/label_download_list_count_prompt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/max_list_count_prompt"
+ android:layout_weight="1.0" />
+ <TextView
+ android:id="@+id/label_download_offset_prompt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/offset_prompt"
+ android:layout_weight="1.0" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/pbap_layout_2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/pbap_layout_1"
+ android:layout_marginTop="5dp" >
+ <EditText
+ android:id="@+id/pbap_download_max_list_count"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:inputType="number"
+ android:layout_weight="1.0" />
+ <EditText
+ android:id="@+id/pbap_download_offset_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:inputType="number"
+ android:layout_weight="1.0" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/pbap_download_filter_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_button"
+ android:layout_below="@+id/pbap_layout_2"
+ android:layout_marginTop="5dp" />
+
+ <Button
+ android:id="@+id/pbap_download_search"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:text="@string/search"
+ android:layout_marginBottom="15dp"
+ android:layout_marginRight="15dp" />
+
+ <Button
+ android:id="@+id/pbap_download_abort"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_toLeftOf="@id/pbap_download_search"
+ android:text="@string/abort"
+ android:layout_marginBottom="15dp"
+ android:layout_marginRight="15dp"
+ android:onClick="onClick_abort" />
+
+ <Button
+ android:id="@+id/pbap_download_getsize"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_toLeftOf="@id/pbap_download_abort"
+ android:text="@string/getsize"
+ android:layout_marginBottom="15dp"
+ android:layout_marginRight="15dp"
+ android:onClick="onClick_download_getsize" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/pbap_download_layout_listview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+ <ListView android:id="@+id/pbap_download_listview_contacts_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <ProgressBar
+ android:id="@+id/pbap_download_progress_bar"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|center_horizontal" />
+
+ <TextView
+ android:id="@+id/pbap_download_test_list_empty"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:text="@string/list_empty" />
+ </LinearLayout>
+ </LinearLayout>
+</RelativeLayout>
+
+<RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="horizontal" >
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/label_browse_phonebook_prompt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:text="@string/phonebook_prompt" />
+
+ <LinearLayout
+ android:id="@+id/pbap_browse_pb_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_below="@+id/label_browse_phonebook_prompt" >
+
+ <Spinner
+ android:id="@+id/pbap_browse_spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:entries="@array/phonebook_arrays"
+ android:prompt="@string/phonebook_prompt" />
+
+ <Button
+ android:id="@+id/pbap_browse_set_phonebook"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/set_phonebook"
+ android:layout_marginRight="15dp"
+ android:tag="pbap_browse_spinner"
+ android:onClick="onClick_setPhonebook" />
+ </LinearLayout>
+
+
+ <TextView
+ android:id="@+id/label_search_attribute_prompt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/pbap_browse_pb_group"
+ android:layout_marginTop="5dp"
+ android:text="@string/search_attribute_prompt" />
+
+ <RadioGroup
+ android:id="@+id/search_attributes"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/label_search_attribute_prompt"
+ android:orientation="horizontal" >
+
+ <RadioButton
+ android:id="@+id/pbap_browse_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/search_attribute_name"
+ android:checked="true" />
+
+ <RadioButton
+ android:id="@+id/pbap_browse_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/search_attribute_number" />
+
+ <RadioButton
+ android:id="@+id/pbap_browse_sound"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/search_attribute_sound" />
+ </RadioGroup>
+
+ <TextView
+ android:id="@+id/label_search_value"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/search_attributes"
+ android:layout_marginTop="5dp"
+ android:text="@string/search_value_prompt" />
+
+ <EditText
+ android:id="@+id/pbap_browse_search_value"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/label_search_value"
+ android:ems="10" />
+
+ <LinearLayout
+ android:id="@+id/pbap_layout_3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/pbap_browse_search_value"
+ android:layout_marginTop="5dp" >
+ <TextView
+ android:id="@+id/label_browse_list_count_prompt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/max_list_count_prompt"
+ android:layout_weight="1.0" />
+ <TextView
+ android:id="@+id/label_browse_offset_prompt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/offset_prompt"
+ android:layout_weight="1.0" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/pbap_layout_4"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/pbap_layout_3"
+ android:layout_marginTop="5dp" >
+ <EditText
+ android:id="@+id/pbap_browse_max_list_count"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:inputType="number"
+ android:layout_weight="1.0" />
+ <EditText
+ android:id="@+id/pbap_browse_offset_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:inputType="number"
+ android:layout_weight="1.0" />
+ </LinearLayout>
+
+ <RadioGroup
+ android:id="@+id/order_attribute"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/pbap_layout_4"
+ android:orientation="horizontal" >
+
+ <RadioButton
+ android:id="@+id/pbap_browse_unordered"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/order_attribute_unordered"
+ android:checked="true" />
+
+ <RadioButton
+ android:id="@+id/pbap_browse_alphabetical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/order_attribute_alphabetical" />
+
+ <RadioButton
+ android:id="@+id/pbap_browse_indexed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/order_attribute_indexed" />
+
+ <RadioButton
+ android:id="@+id/pbap_browse_phonetic"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/order_attribute_phonetic" />
+ </RadioGroup>
+
+ <Button
+ android:id="@+id/pbap_browse_search"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:text="@string/search"
+ android:layout_marginBottom="15dp"
+ android:layout_marginRight="15dp" />
+
+
+ <Button
+ android:id="@+id/pbap_browse_abort"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_toLeftOf="@id/pbap_browse_search"
+ android:text="@string/abort"
+ android:layout_marginBottom="15dp"
+ android:layout_marginRight="15dp"
+ android:onClick="onClick_abort" />
+
+ <Button
+ android:id="@+id/pbap_browse_getsize"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_toLeftOf="@id/pbap_browse_abort"
+ android:text="@string/getsize"
+ android:layout_marginBottom="15dp"
+ android:layout_marginRight="15dp"
+ android:onClick="onClick_browse_getsize" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/pbap_browse_layout_listview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+ <ListView android:id="@+id/pbap_browse_listview_contacts_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <ProgressBar
+ android:id="@+id/pbap_browse_progress_bar"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|center_horizontal" />
+
+ <TextView
+ android:id="@+id/pbap_browse_test_list_empty"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:text="@string/list_empty" />
+ </LinearLayout>
+ </LinearLayout>
+</RelativeLayout>
+
+<RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="horizontal" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="1" >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/phonebook_prompt" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <Spinner
+ android:id="@+id/pbap_vcard_spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:entries="@array/phonebook_arrays"
+ android:prompt="@string/phonebook_prompt" />
+
+ <Button
+ android:id="@+id/pbap_vcard_set_phonebook"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/set_phonebook"
+ android:layout_marginRight="15dp"
+ android:tag="pbap_vcard_spinner"
+ android:onClick="onClick_setPhonebook" />
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/vcard_header_prompt" />
+
+ <EditText
+ android:id="@+id/pbap_vcard_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:inputType="text" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:text="@string/vcard_format_prompt" />
+
+ <RadioGroup
+ android:id="@+id/pbab_vcard_formats"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <RadioButton
+ android:id="@+id/pbap_vcard_vcard21"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/vcard21"
+ android:checked="true"
+ android:onClick="onClickRadioButtonVcardType21" />
+
+ <RadioButton
+ android:id="@+id/pbap_vcard_vcard30"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/vcard30"
+ android:onClick="onClickRadioButtonVcardType30" />
+ </RadioGroup>
+
+ <Button
+ android:id="@+id/pbap_vcard_filter_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_button"
+ android:layout_marginTop="5dp"
+ android:onClick="onClickVcardFilterAttributes"/>
+
+ <Button
+ android:id="@+id/pbap_vcard_get_vcard"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:layout_marginTop="50dp"
+ android:text="@string/get_vcard"
+ android:onClick="onClickVcardButtonGet" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1" >
+
+ <org.codeaurora.bluetooth.bttestapp.VcardView
+ android:id="@+id/pbap_vcard_vcardview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ </LinearLayout>
+ </LinearLayout>
+</RelativeLayout>
+</ViewFlipper>
diff --git a/bttestapp/res/layout/activity_vcard_filter.xml b/bttestapp/res/layout/activity_vcard_filter.xml
new file mode 100644
index 0000000..8b41dcd
--- /dev/null
+++ b/bttestapp/res/layout/activity_vcard_filter.xml
@@ -0,0 +1,202 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:baselineAligned="false" >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+ <CheckBox
+ android:id="@+id/filter_chooser_version"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_version" />
+ <CheckBox
+ android:id="@+id/filter_chooser_fn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_fn" />
+ <CheckBox
+ android:id="@+id/filter_chooser_n"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_n" />
+ <CheckBox
+ android:id="@+id/filter_chooser_photo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_photo" />
+ <CheckBox
+ android:id="@+id/filter_chooser_bday"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_bday" />
+ <CheckBox
+ android:id="@+id/filter_chooser_adr"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_adr" />
+ <CheckBox
+ android:id="@+id/filter_chooser_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_label" />
+ <CheckBox
+ android:id="@+id/filter_chooser_tel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_tel" />
+ <CheckBox
+ android:id="@+id/filter_chooser_email"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_email" />
+ <CheckBox
+ android:id="@+id/filter_chooser_mailer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_mailer" />
+ <CheckBox
+ android:id="@+id/filter_chooser_tz"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_tz" />
+ <CheckBox
+ android:id="@+id/filter_chooser_geo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_geo" />
+ <CheckBox
+ android:id="@+id/filter_chooser_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_title" />
+ <CheckBox
+ android:id="@+id/filter_chooser_role"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_role" />
+ <CheckBox
+ android:id="@+id/filter_chooser_logo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_logo" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <CheckBox
+ android:id="@+id/filter_chooser_agent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_agent" />
+ <CheckBox
+ android:id="@+id/filter_chooser_org"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_org" />
+ <CheckBox
+ android:id="@+id/filter_chooser_note"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_note" />
+ <CheckBox
+ android:id="@+id/filter_chooser_rev"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_rev" />
+ <CheckBox
+ android:id="@+id/filter_chooser_sound"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_sound" />
+ <CheckBox
+ android:id="@+id/filter_chooser_url"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_url" />
+ <CheckBox
+ android:id="@+id/filter_chooser_uid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_uid" />
+ <CheckBox
+ android:id="@+id/filter_chooser_key"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_key" />
+ <CheckBox
+ android:id="@+id/filter_chooser_nickname"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_nickname" />
+ <CheckBox
+ android:id="@+id/filter_chooser_categories"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_categories" />
+ <CheckBox
+ android:id="@+id/filter_chooser_proid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_proid" />
+ <CheckBox
+ android:id="@+id/filter_chooser_class"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_class" />
+ <CheckBox
+ android:id="@+id/filter_chooser_sort_string"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_sort_string" />
+ <CheckBox
+ android:id="@+id/filter_chooser_x_irmc_call_datetime"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_x_irmc_call_datetime" />
+ <CheckBox
+ android:id="@+id/filter_chooser_filter"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/filter_field_filter" />
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView>
diff --git a/bttestapp/res/layout/call_view.xml b/bttestapp/res/layout/call_view.xml
new file mode 100644
index 0000000..a415f41
--- /dev/null
+++ b/bttestapp/res/layout/call_view.xml
@@ -0,0 +1,182 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<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="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/call_id"
+ android:layout_width="50dip"
+ android:layout_height="50dip"
+ android:layout_marginTop="5dp"
+ android:layout_marginBottom="5dp"
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="10dp"
+ android:gravity="center|center_vertical"
+ android:background="@color/call_unknown"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight=".4"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ tools:ignore="UseCompoundDrawables" >
+
+ <ImageView
+ android:id="@+id/call_direction"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ tools:ignore="ContentDescription"/>
+
+ <TextView
+ android:id="@+id/call_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/call_multiparty"
+ style="@style/indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="5dip"
+ android:paddingRight="5dip"
+ android:background="@color/ind_off"
+ android:text="@string/call_multiparty" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight=".6"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ style="@style/indicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+ <TextView
+ android:id="@+id/call_state_active"
+ style="@style/indicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="@color/call_active"
+ android:text="@string/call_state_active" />
+
+ <TextView
+ android:id="@+id/call_state_held"
+ style="@style/indicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="@color/call_held"
+ android:text="@string/call_state_held" />
+
+ <TextView
+ android:id="@+id/call_state_held_by_rnh"
+ style="@style/indicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="@color/call_held"
+ android:text="@string/call_state_held_by_rnh" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/call_state_dialing"
+ style="@style/indicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="@color/call_new"
+ android:text="@string/call_state_dialing" />
+
+ <TextView
+ android:id="@+id/call_state_alerting"
+ style="@style/indicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="@color/call_new"
+ android:text="@string/call_state_alerting" />
+
+ <TextView
+ android:id="@+id/call_state_incoming"
+ style="@style/indicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="@color/call_new"
+ android:text="@string/call_state_incoming" />
+
+ <TextView
+ android:id="@+id/call_state_waiting"
+ style="@style/indicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="@color/call_new"
+ android:text="@string/call_state_waiting" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/calls_list_fragment.xml b/bttestapp/res/layout/calls_list_fragment.xml
new file mode 100644
index 0000000..a3f3eb2
--- /dev/null
+++ b/bttestapp/res/layout/calls_list_fragment.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ListView
+ android:id="@+id/calls_list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/call_action_accept"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_accept" />
+
+ <Button
+ android:id="@+id/call_action_hold_and_accept"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_hold_and_accept" />
+
+ <Button
+ android:id="@+id/call_action_release_and_accept"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_release_and_accept" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/call_action_reject"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_reject" />
+
+ <Button
+ android:id="@+id/call_action_terminate"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_terminate" />
+
+ <Button
+ android:id="@+id/call_action_hold"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_hold" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/call_action_respond_and_hold"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_respond_and_hold" />
+
+ <Button
+ android:id="@+id/call_action_private_mode"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_private_mode" />
+
+ <Button
+ android:id="@+id/call_action_explicit_transfer"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:text="@string/call_action_explicit_transfer" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/date_time_picker.xml b/bttestapp/res/layout/date_time_picker.xml
new file mode 100644
index 0000000..ea567aa
--- /dev/null
+++ b/bttestapp/res/layout/date_time_picker.xml
@@ -0,0 +1,46 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="horizontal" >
+
+ <DatePicker
+ android:id="@+id/date_picker"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:calendarViewShown="false" />
+
+ <TimePicker
+ android:id="@+id/time_picker"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/dialpad_fragment.xml b/bttestapp/res/layout/dialpad_fragment.xml
new file mode 100644
index 0000000..ae33855
--- /dev/null
+++ b/bttestapp/res/layout/dialpad_fragment.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<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="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <EditText
+ android:id="@+id/dialpad_number"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:editable="false"
+ android:singleLine="true"
+ android:ellipsize="start"
+ android:scrollHorizontally="true"
+ android:hint="@string/dialpad_number_hint"
+ tools:ignore="LabelFor,TextFields,Deprecated" />
+
+ <ImageButton
+ android:id="@+id/dialpad_del"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/dialpad_del"
+ android:src="@drawable/sym_keyboard_delete" />
+
+ </LinearLayout>
+
+ <GridView
+ android:id="@+id/dialpad"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:numColumns="4" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical" >
+
+ <Button
+ android:id="@+id/dialpad_dial"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.5"
+ android:text="@string/dialpad_dial" />
+
+ <Button
+ android:id="@+id/dialpad_memdial"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.5"
+ android:text="@string/dialpad_memdial" />
+
+ <ToggleButton
+ android:id="@+id/dialpad_dtmf"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textOn="@string/dialpad_dtmf"
+ android:textOff="@string/dialpad_dtmf" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/indicators_fragment.xml b/bttestapp/res/layout/indicators_fragment.xml
new file mode 100644
index 0000000..473bc81
--- /dev/null
+++ b/bttestapp/res/layout/indicators_fragment.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<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="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <ToggleButton
+ android:id="@+id/ind_conn_state"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textOn="@string/ind_conn_state"
+ android:textOff="@string/ind_conn_state" />
+
+ <ToggleButton
+ android:id="@+id/ind_audio_state"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textOn="@string/ind_audio_state"
+ android:textOff="@string/ind_audio_state" />
+
+ <ToggleButton
+ android:id="@+id/ind_vr_state"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textOn="@string/ind_vr_state"
+ android:textOff="@string/ind_vr_state" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/ind_operator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/ind_operator_unknown"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/ind_subscriber"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/ind_subscriber_unknown" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="right"
+ android:text="@string/ind_signal_level"
+ style="@style/indicator"
+ android:background="#00000000" />
+
+ <TextView
+ android:id="@+id/ind_signal_level"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="left"
+ style="@style/indicator"
+ android:background="#00000000" />
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="right"
+ android:text="@string/ind_battery_level"
+ style="@style/indicator"
+ android:background="#00000000" />
+
+ <TextView
+ android:id="@+id/ind_battery_level"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="left"
+ style="@style/indicator"
+ android:background="#00000000" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/ind_network_state"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/ind_network_state"
+ style="@style/indicator" />
+
+ <TextView
+ android:id="@+id/ind_roaming_state"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/ind_roaming_state"
+ style="@style/indicator" />
+
+ <TextView
+ android:id="@+id/ind_inband_state"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/ind_inband_state"
+ style="@style/indicator" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/message_row.xml b/bttestapp/res/layout/message_row.xml
new file mode 100644
index 0000000..11a051b
--- /dev/null
+++ b/bttestapp/res/layout/message_row.xml
@@ -0,0 +1,179 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp" >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#303030"
+ style="@style/someSmallPadding" >
+
+ <TextView
+ android:id="@+id/message_row_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="20dp"
+ android:typeface="monospace"
+ android:layout_alignParentLeft="true" />
+
+ <TextView
+ android:id="@+id/message_row_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="6"
+ android:typeface="monospace"
+ android:paddingRight="20dp"
+ android:layout_toRightOf="@id/message_row_date" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/message_row_type"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/message_row_flag_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="3dp"
+ android:typeface="monospace"
+ android:text="@string/msgflag_text" />
+
+ <TextView
+ android:id="@+id/message_row_flag_read"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dp"
+ android:layout_marginRight="3dp"
+ android:typeface="monospace"
+ android:text="@string/msgflag_read" />
+
+ <TextView
+ android:id="@+id/message_row_flag_sent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dp"
+ android:layout_marginRight="3dp"
+ android:typeface="monospace"
+ android:text="@string/msgflag_sent" />
+
+ <TextView
+ android:id="@+id/message_row_flag_drm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dp"
+ android:layout_marginRight="3dp"
+ android:typeface="monospace"
+ android:text="@string/msgflag_drm" />
+
+ <TextView
+ android:id="@+id/message_row_flag_prio"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dp"
+ android:typeface="monospace"
+ android:text="@string/msgflag_prio" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/message_row_handle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="monospace"
+ android:layout_alignParentRight="true" />
+
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/message_row_subject"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:background="#202020"
+ style="@style/someSmallPadding" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="#202020"
+ style="@style/someSmallPadding" >
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/message_row_from_lbl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:layout_marginRight="5dp"
+ android:text="@string/map_msg_row_from" />
+
+ <TextView
+ android:id="@+id/message_row_from"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/message_row_to_lbl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:layout_marginRight="5dp"
+ android:text="@string/map_msg_row_to" />
+
+ <TextView
+ android:id="@+id/message_row_to"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/messages_filter.xml b/bttestapp/res/layout/messages_filter.xml
new file mode 100644
index 0000000..ab205da
--- /dev/null
+++ b/bttestapp/res/layout/messages_filter.xml
@@ -0,0 +1,235 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_without_message_type" />
+
+ <CheckBox
+ android:id="@+id/map_filter_type_sms_gsm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_type_sms_gsm" />
+
+ <CheckBox
+ android:id="@+id/map_filter_type_sms_cdma"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_type_sms_cdma" />
+
+ <CheckBox
+ android:id="@+id/map_filter_type_email"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_type_email" />
+
+ <CheckBox
+ android:id="@+id/map_filter_type_mms"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_msg_type_mms" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_read_status" />
+
+ <RadioGroup
+ android:id="@+id/map_filter_read_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <RadioButton
+ android:id="@+id/map_filter_read_status_all"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_any_status"
+ android:checked="true" />
+
+ <RadioButton
+ android:id="@+id/map_filter_read_status_unread"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_read_status_unread" />
+
+ <RadioButton
+ android:id="@+id/map_filter_read_status_read"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_read_status_read" />
+
+ </RadioGroup>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_priority" />
+
+ <RadioGroup
+ android:id="@+id/map_filter_priority"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <RadioButton
+ android:id="@+id/map_filter_priority_all"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_any_priority"
+ android:checked="true" />
+
+ <RadioButton
+ android:id="@+id/map_filter_priority_high"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_priority_high" />
+
+ <RadioButton
+ android:id="@+id/map_filter_status_non_high"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_priority_non_high" />
+
+ </RadioGroup>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <TableLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusableInTouchMode="true"
+ android:stretchColumns="1" >
+
+ <TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_period_begin" />
+
+ <EditText
+ android:id="@+id/map_filter_period_begin"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <ImageButton
+ android:id="@+id/map_pick_data_period_begin"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@android:drawable/ic_menu_today" />
+
+ </TableRow>
+
+ <TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_period_end" />
+
+ <EditText
+ android:id="@+id/map_filter_period_end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <ImageButton
+ android:id="@+id/map_pick_data_period_end"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@android:drawable/ic_menu_today" />
+ </TableRow>
+
+ </TableLayout>
+
+ <TableLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:stretchColumns="1" >
+
+ <TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_recipient" />
+
+ <EditText
+ android:id="@+id/map_filter_recipient"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </TableRow>
+
+ <TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/map_filter_originator" />
+
+ <EditText
+ android:id="@+id/map_filter_originator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </TableRow>
+ </TableLayout>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/pbap_auth.xml b/bttestapp/res/layout/pbap_auth.xml
new file mode 100644
index 0000000..b3f7988
--- /dev/null
+++ b/bttestapp/res/layout/pbap_auth.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright (c) 2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:singleLine="true" />
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/bttestapp/res/layout/service_row.xml b/bttestapp/res/layout/service_row.xml
new file mode 100644
index 0000000..975b0e6
--- /dev/null
+++ b/bttestapp/res/layout/service_row.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <LinearLayout
+ android:id="@+id/service_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true" >
+
+ <TextView
+ android:id="@+id/service_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/service_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </LinearLayout>
+
+ <Switch
+ android:id="@+id/service_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:focusable="false" />
+
+ <Switch
+ android:id="@+id/notification_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toLeftOf="@id/service_switch"
+ android:focusable="false"
+ android:visibility="gone" />
+
+</RelativeLayout>
diff --git a/bttestapp/res/layout/string_picker_dialog.xml b/bttestapp/res/layout/string_picker_dialog.xml
new file mode 100644
index 0000000..2ca09d5
--- /dev/null
+++ b/bttestapp/res/layout/string_picker_dialog.xml
@@ -0,0 +1,41 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <EditText
+ android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="text"/>
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/vcard_row.xml b/bttestapp/res/layout/vcard_row.xml
new file mode 100644
index 0000000..5458a9b
--- /dev/null
+++ b/bttestapp/res/layout/vcard_row.xml
@@ -0,0 +1,56 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="4dip" >
+
+ <ImageView
+ android:id="@+id/vcard_photo"
+ android:contentDescription="@string/blank"
+ android:layout_width="50dip"
+ android:layout_height="50dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerCrop"
+ android:src="@drawable/ic_contact_picture" />
+
+ <TextView
+ android:id="@+id/vcard_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="15dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="@string/blank"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</LinearLayout>
diff --git a/bttestapp/res/layout/vcard_view.xml b/bttestapp/res/layout/vcard_view.xml
new file mode 100644
index 0000000..4a9a4e6
--- /dev/null
+++ b/bttestapp/res/layout/vcard_view.xml
@@ -0,0 +1,90 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scrollView1"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="15dp" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="15dp"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/contact_photo"
+ android:contentDescription="@string/blank"
+ android:layout_width="50dip"
+ android:layout_height="50dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerCrop"
+ android:src="@drawable/ic_contact_picture" />
+
+ <TextView
+ android:id="@+id/contact_display_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingLeft="15dp"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/contact_phonelist_hdr"
+ style="?android:attr/listSeparatorTextViewStyle" />
+ <LinearLayout
+ android:id="@+id/contact_phonelist"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/contact_emaillist_hdr"
+ style="?android:attr/listSeparatorTextViewStyle" />
+ <LinearLayout
+ android:id="@+id/contact_emaillist"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/bttestapp/res/menu/menu_filter.xml b/bttestapp/res/menu/menu_filter.xml
new file mode 100644
index 0000000..1ffb500
--- /dev/null
+++ b/bttestapp/res/menu/menu_filter.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/menu_save"
+ android:icon="@drawable/ic_menu_save"
+ android:title="@string/menu_save"
+ android:showAsAction="withText|ifRoom" />
+
+</menu>
diff --git a/bttestapp/res/menu/menu_hfp_test.xml b/bttestapp/res/menu/menu_hfp_test.xml
new file mode 100644
index 0000000..bea483f
--- /dev/null
+++ b/bttestapp/res/menu/menu_hfp_test.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/menu_call_history"
+ android:title="@string/menu_call_history"
+ android:showAsAction="ifRoom|withText" />
+
+ <item android:id="@+id/menu_request_phone_num"
+ android:title="@string/menu_request_phone_num"
+ android:showAsAction="ifRoom|withText" />
+
+</menu>
diff --git a/bttestapp/res/menu/menu_map_test.xml b/bttestapp/res/menu/menu_map_test.xml
new file mode 100644
index 0000000..e9ac920
--- /dev/null
+++ b/bttestapp/res/menu/menu_map_test.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- Three-dots submenu items -->
+ <item android:id="@+id/menu_map_update_inbox"
+ android:title="@string/map_update_inbox"
+ android:showAsAction="never"
+ android:visible="false" />
+
+ <item android:id="@+id/menu_map_connect"
+ android:title="@string/menu_connect"
+ android:showAsAction="never"
+ android:visible="false" />
+
+ <item android:id="@+id/menu_map_disconnect"
+ android:title="@string/menu_disconnect"
+ android:showAsAction="never"
+ android:visible="false" />
+
+ <item
+ android:id="@+id/menu_map_goto_inbox"
+ android:title="@string/map_goto_inbox"
+ android:showAsAction="never" />
+
+ <item
+ android:id="@+id/menu_map_goto_outbox"
+ android:title="@string/map_goto_outbox"
+ android:showAsAction="never" />
+
+ <item
+ android:id="@+id/menu_map_goto_draft"
+ android:title="@string/map_goto_draft"
+ android:showAsAction="never" />
+</menu>
diff --git a/bttestapp/res/menu/menu_pbap_test.xml b/bttestapp/res/menu/menu_pbap_test.xml
new file mode 100644
index 0000000..c117c17
--- /dev/null
+++ b/bttestapp/res/menu/menu_pbap_test.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/menu_pbap_connect"
+ android:icon="@drawable/ic_bt_disconnected"
+ android:title="@string/menu_connect"
+ android:showAsAction="withText|always"
+ android:visible="false" />
+
+ <item android:id="@+id/menu_pbap_disconnect"
+ android:icon="@drawable/ic_bt_connected"
+ android:title="@string/menu_disconnect"
+ android:showAsAction="withText|always"
+ android:visible="false" />
+</menu>
diff --git a/bttestapp/res/values-v11/styles.xml b/bttestapp/res/values-v11/styles.xml
new file mode 100644
index 0000000..e8dcb93
--- /dev/null
+++ b/bttestapp/res/values-v11/styles.xml
@@ -0,0 +1,40 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/bttestapp/res/values-v14/styles.xml b/bttestapp/res/values-v14/styles.xml
new file mode 100644
index 0000000..f316629
--- /dev/null
+++ b/bttestapp/res/values-v14/styles.xml
@@ -0,0 +1,41 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="@android:style/Theme.DeviceDefault">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/bttestapp/res/values/colors.xml b/bttestapp/res/values/colors.xml
new file mode 100644
index 0000000..4011f13
--- /dev/null
+++ b/bttestapp/res/values/colors.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+ <!--
+ <color name="color_white">#ffffff</color>
+ -->
+
+ <color name="ind_on">#008000</color>
+ <color name="ind_off">#404040</color>
+ <color name="ind_text_unknown">#404040</color>
+ <color name="ind_bar_full">#008000</color>
+ <color name="ind_bar_medium">#804000</color>
+ <color name="ind_bar_low">#800000</color>
+ <color name="ind_bar_off">#404040</color>
+
+ <color name="call_unknown">#202020</color>
+ <color name="call_new">#000080</color>
+ <color name="call_held">#804000</color>
+ <color name="call_active">#008000</color>
+ <color name="call_selected">#50800000</color>
+
+</resources>
diff --git a/bttestapp/res/values/dimens.xml b/bttestapp/res/values/dimens.xml
new file mode 100644
index 0000000..5a78874
--- /dev/null
+++ b/bttestapp/res/values/dimens.xml
@@ -0,0 +1,36 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/bttestapp/res/values/strings.xml b/bttestapp/res/values/strings.xml
new file mode 100644
index 0000000..cf8bcc0
--- /dev/null
+++ b/bttestapp/res/values/strings.xml
@@ -0,0 +1,330 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+ <item type="id" name="dialpad_button" />
+ <item type="id" name="dialpad_button_pos" />
+
+<!--
+<string name=""></string>
+-->
+ <!-- General -->
+ <string name="app_name">BTTestApp</string>
+ <string name="blank"></string>
+ <string name="cancel">Cancel</string>
+
+ <string name="ppl_add">+</string>
+ <string name="ppl_del">-</string>
+
+ <!-- Main -->
+ <string name="services_header">Available services</string>
+ <string name="select_device">Select</string>
+ <string name="discover_services">Discover</string>
+ <string name="clear">Clear</string>
+ <string name="list_empty">No records found!</string>
+
+ <!-- Dialogs -->
+ <string name="dialog_title_create_msg_filter">Create message filter:</string>
+ <string name="dialog_title_pick_date_time">Pick date and time:</string>
+
+ <!-- Titles -->
+ <string name="title_hfp_test">HFP Test</string>
+ <string name="title_pbap_test">PBAP Test</string>
+ <string name="title_map_test">MAP Test</string>
+ <string name="title_vcard_filter">Filter Attributes</string>
+ <string name="title_message_filter">Select Message Parameters</string>
+
+ <!-- PBAP -->
+ <string name="phonebook_prompt">Choose a phonebook:</string>
+ <string name="vcard_format_prompt">Choose a vCard format:</string>
+ <string name="max_list_count_prompt">MaxListCount:</string>
+ <string name="vcard_header_prompt">vCard header:</string>
+ <string name="offset_prompt">Offset value:</string>
+ <string name="search">Search</string>
+ <string name="getsize">Get size</string>
+ <string name="abort">Abort</string>
+ <string name="pbap_session_key_dialog_header">OBEX session key required</string>
+ <string name="pbap_session_key_dialog_title">Type session key for %1$s</string>
+ <string name="pbap_authentication_timeout_message">There was time out to input session key with %1$s</string>
+ <string name="auth_notif_ticker">OBEX authentication request</string>
+ <string name="auth_notif_title">Session key</string>
+ <string name="auth_notif_message">Type session key for %1$s</string>
+ <string name="get_vcard">Get vCard</string>
+
+ <string name="search_attribute_prompt">Search Attribute:</string>
+ <string name="search_value_prompt">Search Value:</string>
+ <string name="search_attribute_name">Name</string>
+ <string name="search_attribute_number">Number</string>
+
+ <string name="order_attribute_unordered">Default</string>
+ <string name="order_attribute_alphabetical">Alphabetical</string>
+ <string name="order_attribute_indexed">Indexed</string>
+ <string name="order_attribute_phonetic">Phonetic</string>
+ <string name="search_attribute_sound">Sound</string>
+
+ <string name="filter_button">Select Filter Attributes</string>
+ <string name="filter_field_version">vCard Version</string>
+ <string name="filter_field_fn">Formatted Name</string>
+ <string name="filter_field_n">Structured Presentation of Name</string>
+ <string name="filter_field_photo">Associated Image or Photo</string>
+ <string name="filter_field_bday">Birthday</string>
+ <string name="filter_field_adr">Delivery Address</string>
+ <string name="filter_field_label">Delivery</string>
+ <string name="filter_field_tel">Telephone Number</string>
+ <string name="filter_field_email">Electronic Mail Address</string>
+ <string name="filter_field_mailer">Electronic Mail</string>
+ <string name="filter_field_tz">Time Zone</string>
+ <string name="filter_field_geo">Geographic Position</string>
+ <string name="filter_field_title">Job</string>
+ <string name="filter_field_role">Role within the Organization</string>
+ <string name="filter_field_logo">Organization Logo</string>
+ <string name="filter_field_agent">vCard of Person Representing</string>
+ <string name="filter_field_org">Name of Organization</string>
+ <string name="filter_field_note">Comments</string>
+ <string name="filter_field_rev">Revision</string>
+ <string name="filter_field_sound">Pronunciation of Name</string>
+ <string name="filter_field_url">Uniform Resource Locator</string>
+ <string name="filter_field_uid">Unique ID</string>
+ <string name="filter_field_key">Public Encryption Key</string>
+ <string name="filter_field_nickname">Nickname</string>
+ <string name="filter_field_categories">Categories</string>
+ <string name="filter_field_proid">Product ID</string>
+ <string name="filter_field_class">Class information</string>
+ <string name="filter_field_sort_string">String used for sorting operations</string>
+ <string name="filter_field_x_irmc_call_datetime">Time stamp</string>
+ <string name="filter_field_filter">Indicates the usage of a filter</string>
+
+ <string name="vcard21">vCard 2.1</string>
+ <string name="vcard30">vCard 3.0</string>
+
+ <string name="set_phonebook">SetPhonebook</string>
+
+ <!-- HLS -->
+
+ <!-- MAP -->
+ <string name="msgflag_text">TEXT</string>
+ <string name="msgflag_read">READ</string>
+ <string name="msgflag_sent">SENT</string>
+ <string name="msgflag_drm">DRM</string>
+ <string name="msgflag_prio">PRIO</string>
+ <string name="map_goto_inbox">go to inbox</string>
+ <string name="map_goto_outbox">go to outbox</string>
+ <string name="map_goto_draft">go to draft</string>
+ <string name="map_msg_copy_to_editor_lbl">Copy ></string>
+ <string name="map_msg_push_lbl">Push message ></string>
+ <string name="map_msg_push_transparent_lbl">transparent</string>
+ <string name="map_msg_push_retry_lbl">retry</string>
+ <string name="charset_native_lbl">native</string>
+ <string name="charset_utf8_lbl">UTF-8</string>
+ <string name="map_msg_get_lbl">Get message ></string>
+ <string name="map_msg_get_attachment_lbl">w/attach</string>
+ <string name="map_folder_root">/</string>
+ <string name="map_folder_up">..</string>
+ <string name="map_folder_enter">-></string>
+ <string name="map_folder_listing">list</string>
+ <string name="map_folder_listing_size">size</string>
+ <string name="map_message_listing">Get Message Listing</string>
+ <string name="map_message_listing_size">Get Message Listing Size</string>
+ <string name="map_update_inbox">Update Inbox</string>
+ <string name="map_delete_message">Delete</string>
+ <string name="map_max_list_count_prompt">Max list count:</string>
+ <string name="map_list_start_offset_prompt">Start offset:</string>
+ <string name="map_subject_length_prompt">Subject length:</string>
+ <string name="map_parameter_subject">Subject</string>
+ <string name="map_parameter_datetime">Datetime</string>
+ <string name="map_parameter_sender_name">Sender Name</string>
+ <string name="map_parameter_sender_addressing">Sender Addressing</string>
+ <string name="map_parameter_recipient_name">Recipient Name</string>
+ <string name="map_parameter_recipient_addressing">Recipient Addressing</string>
+ <string name="map_parameter_type">Type</string>
+ <string name="map_parameter_size">Size</string>
+ <string name="map_parameter_reception_status">Reception Status</string>
+ <string name="map_parameter_text">Text</string>
+ <string name="map_parameter_attachment_size">Attachment Size</string>
+ <string name="map_parameter_priority">Priority</string>
+ <string name="map_parameter_read">Read</string>
+ <string name="map_parameter_sent">Sent</string>
+ <string name="map_parameter_protected">Protected</string>
+ <string name="map_parameter_replyto_addressing">Replayto Addressing</string>
+ <string name="map_message_parameters">Parameters</string>
+ <string name="map_message_filter">Filter</string>
+ <string name="map_msg_row_from"><</string>
+ <string name="map_msg_row_to">></string>
+ <string name="map_msg_type_sms_gsm">SMS GSM</string>
+ <string name="map_msg_type_sms_cdma">SMS CDMA</string>
+ <string name="map_msg_type_email">EMAIL</string>
+ <string name="map_msg_type_mms">MMS</string>
+ <string name="map_filter_any_status">Any status</string>
+ <string name="map_filter_any_priority">Any priority</string>
+ <string name="map_filter_without_message_type">Filter out message type:</string>
+ <string name="map_filter_read_status">Read status:</string>
+ <string name="map_filter_read_status_unread">Unread</string>
+ <string name="map_filter_read_status_read">Read</string>
+ <string name="map_filter_priority">Priority:</string>
+ <string name="map_filter_priority_high">High</string>
+ <string name="map_filter_priority_non_high">Non-high</string>
+ <string name="map_filter_period_begin">Period begin:</string>
+ <string name="map_filter_period_end">Period end:</string>
+ <string name="map_filter_recipient">Recipient:</string>
+ <string name="map_filter_originator">Originator:</string>
+ <string name="map_messages_list_empty">No messages found!</string>
+ <string name="map_set_read">Set READ</string>
+ <string name="map_set_unread">Set UNREAD</string>
+ <string name="map_recipients_prompt">Recipients:</string>
+ <string name="map_originators_prompt">Originators:</string>
+ <string name="map_bmsg_status_prompt">Status:</string>
+ <string name="map_bmsg_type_prompt">Type:</string>
+ <string name="map_bmsg_folder_prompt">Folder:</string>
+ <string name="map_bbody_encoding_prompt">Encoding:</string>
+ <string name="map_bbody_charset_prompt">Charset:</string>
+ <string name="map_bbody_language_prompt">Language:</string>
+ <string name="map_report_notif_ticker">New MAP notification report received!</string>
+ <string name="map_report_notif_title_delivery_success">Delivery success</string>
+ <string name="map_report_notif_title_sending_success">Sending success</string>
+ <string name="map_report_notif_title_delivery_failure">Delivery failure</string>
+ <string name="map_report_notif_title_sending_failure">Sending failure</string>
+ <string name="map_report_notif_title_memory_full">Memory full</string>
+ <string name="map_report_notif_title_memory_available">Memory available</string>
+ <string name="map_report_notif_title_message_deleted">Message deleted</string>
+ <string name="map_report_notif_received">Received new %1$s message</string>
+ <string name="map_report_notif_handle">Handle: %1$s</string>
+ <string name="map_report_notif_shifted">Message %1$s shifted</string>
+ <string name="map_report_notif_fromto">From %1$s to %2$s</string>
+
+ <string-array name="map_message_type_array">
+ <item>SMS_GSM</item>
+ <item>SMS_CDMA</item>
+ <item>EMAIL</item>
+ <item>MMS</item>
+ </string-array>
+
+ <string-array name="map_message_encoding_array">
+ <item></item>
+ <item>8BIT</item>
+ <item>G-7BIT</item>
+ <item>G-7BITEXT</item>
+ <item>G-UCS2</item>
+ <item>G-8BIT</item>
+ <item>C-8BIT</item>
+ <item>C-EPM</item>
+ <item>C-7ASCII</item>
+ <item>C-IA5</item>
+ <item>C-UNICODE</item>
+ <item>C-SJIS</item>
+ <item>C-KOREAN</item>
+ <item>C-LATINHEB</item>
+ <item>C-LATIN</item>
+ </string-array>
+
+ <string-array name="phonebook_arrays">
+ <item>telecom/pb.vcf</item>
+ <item>telecom/ich.vcf</item>
+ <item>telecom/och.vcf</item>
+ <item>telecom/mch.vcf</item>
+ <item>telecom/cch.vcf</item>
+ <item>SIM1/telecom/pb.vcf</item>
+ <item>SIM1/telecom/ich.vcf</item>
+ <item>SIM1/telecom/och.vcf</item>
+ <item>SIM1/telecom/mch.vcf</item>
+ <item>SIM1/telecom/cch.vcf</item>
+ </string-array>
+
+ <!-- Contact Details -->
+ <string name="contact_phonelist_hdr">Phone list</string>
+ <string name="contact_emaillist_hdr">E-mail list</string>
+
+ <!-- Menu -->
+ <string name="menu_save">Save</string>
+ <string name="menu_connect">Connect</string>
+ <string name="menu_disconnect">Disconnect</string>
+
+ <!-- Other -->
+ <string name="vcard">vCard</string>
+ <string name="download">Download</string>
+ <string name="browse">Browse</string>
+
+ <!-- Toast messages -->
+ <string name="msg_connection_success">Device has been connected.</string>
+ <string name="msg_disconnection_success">Device has been disconnected.</string>
+
+
+ <!-- HFP test activity -->
+ <string name="hfptest_call_state_unknown_text">unknown</string>
+ <string name="menu_request_phone_num">Request phone number</string>
+ <string name="menu_call_history">Call history</string>
+ <string name="call_history_title">Select number:</string>
+ <string name="dialpad_number_hint">Click "<sup><small>Re</small></sup>Dial" to redial</string>
+ <string name="dialpad_del">Delete</string>
+ <string name="dialpad_dial"><sup><small>Re</small></sup>Dial</string>
+ <string name="dialpad_memdial">Mem dial</string>
+ <string name="dialpad_dtmf">DTMF</string>
+ <string name="ind_conn_state">CONN</string>
+ <string name="ind_audio_state">AUDIO</string>
+ <string name="ind_vr_state">VR</string>
+ <string name="ind_inband_state">IN-BAND</string>
+ <string name="ind_network_state">NETWORK</string>
+ <string name="ind_roaming_state">ROAMING</string>
+ <string name="ind_signal_level">signal</string>
+ <string name="ind_battery_level">battery</string>
+ <string name="ind_operator_unknown">no operator info</string>
+ <string name="ind_subscriber_unknown">no subscriber info</string>
+ <string name="ind_bar">▌▌▌▌▌</string>
+ <string name="call_state_active">ACTIVE</string>
+ <string name="call_state_held">HELD</string>
+ <string name="call_state_dialing">DIALING</string>
+ <string name="call_state_alerting">ALERTING</string>
+ <string name="call_state_incoming">INCOMING</string>
+ <string name="call_state_waiting">WAITING</string>
+ <string name="call_state_held_by_rnh">R&H</string>
+ <string name="call_multiparty">MULTIPARTY</string>
+ <string name="call_action_accept">Accept</string>
+ <string name="call_action_accept_held">Accept held</string>
+ <string name="call_action_merge">Merge calls</string>
+ <string name="call_action_hold_and_accept">Hold and accept</string>
+ <string name="call_action_swap">Swap calls</string>
+ <string name="call_action_release_and_accept">Release and accept</string>
+ <string name="call_action_reject">Reject</string>
+ <string name="call_action_reject_held">Reject held</string>
+ <string name="call_action_terminate">Terminate</string>
+ <string name="call_action_hold">Hold</string>
+ <string name="call_action_respond_and_hold">Respond and hold</string>
+ <string name="call_action_private_mode">Private mode</string>
+ <string name="call_action_explicit_transfer">Explicit transfer</string>
+
+ <!-- HFP result codes messages -->
+ <string name="hfptest_result_ok_text">OK</string>
+ <string name="hfptest_result_error_text">Error</string>
+ <string name="hfptest_result_no_carrier_text">Error: no carrier</string>
+ <string name="hfptest_result_busy_text">Error: busy</string>
+ <string name="hfptest_result_no_answer_text">Error: no answar</string>
+ <string name="hfptest_result_delayed_text">Error: delayed</string>
+ <string name="hfptest_result_blacklisted_text">Error: blacklisted</string>
+ <string name="hfptest_result_cme_text">Error: CME</string>
+</resources>
diff --git a/bttestapp/res/values/styles.xml b/bttestapp/res/values/styles.xml
new file mode 100644
index 0000000..00ea0d5
--- /dev/null
+++ b/bttestapp/res/values/styles.xml
@@ -0,0 +1,78 @@
+<!--
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+ <style name="TextViewAsLabel">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:background">#525252</item>
+ <item name="android:textColor">#c0c0c0</item>
+ <item name="android:layout_marginTop">3dp</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="somePadding">
+ <item name="android:paddingLeft">20dp</item>
+ <item name="android:paddingRight">20dp</item>
+ </style>
+
+ <style name="someSmallPadding">
+ <item name="android:paddingLeft">10dp</item>
+ <item name="android:paddingRight">10dp</item>
+ <item name="android:paddingTop">2dp</item>
+ <item name="android:paddingBottom">2dp</item>
+ </style>
+
+ <style name="indicator">
+ <item name="android:layout_marginTop">5dp</item>
+ <item name="android:layout_marginBottom">5dp</item>
+ <item name="android:layout_marginLeft">5dp</item>
+ <item name="android:layout_marginRight">5dp</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+</resources>
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ActivityHelper.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ActivityHelper.java
new file mode 100644
index 0000000..f91ae3d
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ActivityHelper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+public class ActivityHelper {
+ public static void initialize(Activity activity, int layout) {
+ // Do all sorts of common task for your activities here including:
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+ activity.setContentView(layout);
+ activity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+ activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME
+ | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+ }
+
+ public static void setActionBarTitle(Activity activity, int title) {
+ activity.getActionBar().setTitle(title);
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/BluetoothConnectionReceiver.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/BluetoothConnectionReceiver.java
new file mode 100644
index 0000000..0f276c8
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/BluetoothConnectionReceiver.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+
+import java.util.ArrayList;
+
+public class BluetoothConnectionReceiver extends BroadcastReceiver {
+
+ private final static String TAG = "BluetoothConnectionReceiver";
+
+ public static final String ACTION_NEW_BLUETOOTH_DEVICE = "com.qualcomm.bluetooth.action.NEW_BLUETOOTH_DEVICE";
+
+ public static final String EXTRA_DEVICE_ADDRESS = "com.qualcomm.bluetooth.extra.DEVICE_ADDRESS";
+
+ private static ArrayList<IBluetoothConnectionObserver> observers = new ArrayList<IBluetoothConnectionObserver>();
+
+ private static BluetoothDevice selectedDevice = null;
+
+ public static void registerObserver(IBluetoothConnectionObserver observer) {
+ observers.add(observer);
+
+ if (selectedDevice != null) {
+ observer.onDeviceChanged(selectedDevice);
+ }
+ }
+
+ public static void removeObserver(IBluetoothConnectionObserver observer) {
+ observers.remove(observer);
+ }
+
+ private void notifyObserversDeviceChanged(BluetoothDevice device) {
+ for (IBluetoothConnectionObserver observer : observers) {
+ observer.onDeviceChanged(device);
+ }
+ }
+
+ private void notifyObserversDeviceDisconected() {
+ for (IBluetoothConnectionObserver observer : observers) {
+ observer.onDeviceDisconected();
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_NEW_BLUETOOTH_DEVICE.equals(intent.getAction())) {
+ Logger.v(TAG, "Receive new bluetooth device.");
+
+ String address = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
+
+ if (address != null) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ selectedDevice = adapter.getRemoteDevice(address);
+ notifyObserversDeviceChanged(selectedDevice);
+ } else {
+ Logger.e(TAG, "Received NULL address!");
+ }
+ } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(intent.getAction())) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ if (device.equals(selectedDevice)) {
+ Logger.v(TAG, "Received bluetooth disconected.");
+
+ notifyObserversDeviceDisconected();
+ }
+ } else {
+ Logger.w(TAG, "Unknown intent received with action: " + intent.getAction());
+ }
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallHistoryDialogFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallHistoryDialogFragment.java
new file mode 100644
index 0000000..8e1cda8
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallHistoryDialogFragment.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+
+import java.util.ArrayList;
+
+class CallHistoryDialogFragment extends DialogFragment {
+
+ private final ArrayList<String> mElements;
+
+ private CallHistoryDialogListener mListener;
+
+ public interface CallHistoryDialogListener {
+ public void onCallHistoryDialogPositive(DialogFragment dialog, String number);
+ };
+
+ public CallHistoryDialogFragment(ArrayList<String> elements) {
+ super();
+
+ mElements = new ArrayList<String>(elements);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ CharSequence[] elements = mElements.toArray(new CharSequence[mElements.size()]);
+
+ builder.setTitle(R.string.call_history_title);
+ builder.setItems(elements, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mListener.onCallHistoryDialogPositive(CallHistoryDialogFragment.this,
+ mElements.get(which));
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mListener = (CallHistoryDialogListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement "
+ + CallHistoryDialogListener.class.getSimpleName());
+ }
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallsListFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallsListFragment.java
new file mode 100644
index 0000000..9c87abd
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/CallsListFragment.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*package org.codeaurora.bttestapp;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothHandsfreeClientCall;
+import android.bluetooth.BluetoothProfile;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.qualcomm.bttestapp.R;
+
+public class CallsListFragment extends Fragment implements OnClickListener, OnItemClickListener {
+
+ private final static String TAG = "CallsListFragment";
+
+ private HfpTestActivity mActivity;
+
+ private ListView mCallsList;
+
+ private Button mActionAccept;
+
+ private Button mActionHoldAndAccept;
+
+ private Button mActionReleaseAndAccept;
+
+ private Button mActionReject;
+
+ private Button mActionTerminate;
+
+ private Button mActionHold;
+
+ private Button mActionRespondAndHold;
+
+ private Button mActionPrivateMode;
+
+ private Button mActionExplicitTransfer;
+
+ private class CallsAdapter extends BaseAdapter {
+
+ private final SparseArray<BluetoothHandsfreeClientCall> mCalls;
+
+ private int mSelectedId = 0;
+
+ CallsAdapter() {
+ mCalls = new SparseArray<BluetoothHandsfreeClientCall>();
+ }
+
+ @Override
+ public int getCount() {
+ return mCalls.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mCalls.get(mCalls.keyAt(position));
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mCalls.keyAt(position);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = convertView;
+
+ BluetoothHandsfreeClientCall call = (BluetoothHandsfreeClientCall) getItem(position);
+
+ if (view == null) {
+ view = getActivity().getLayoutInflater().inflate(R.layout.call_view, parent, false);
+ }
+
+ if (mSelectedId == call.getId()) {
+ view.setBackgroundResource(R.drawable.selected_call_bg);
+ } else {
+ view.setBackgroundResource(0);
+ }
+
+ TextView callId = (TextView) view.findViewById(R.id.call_id);
+ callId.setText(Integer.toString(call.getId()));
+
+ ImageView dir = (ImageView) view.findViewById(R.id.call_direction);
+ dir.setImageResource(call.isOutgoing() ? R.drawable.ic_call_outgoing_holo_dark
+ : R.drawable.ic_call_incoming_holo_dark);
+
+ ((TextView) view.findViewById(R.id.call_number)).setText(call.getNumber());
+
+ switch (call.getState()) {
+ case BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE:
+ callId.setBackgroundColor(getColor(R.color.call_active));
+ break;
+
+ case BluetoothHandsfreeClientCall.CALL_STATE_HELD:
+ case BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
+ callId.setBackgroundColor(getColor(R.color.call_held));
+ break;
+
+ case BluetoothHandsfreeClientCall.CALL_STATE_DIALING:
+ case BluetoothHandsfreeClientCall.CALL_STATE_ALERTING:
+ case BluetoothHandsfreeClientCall.CALL_STATE_INCOMING:
+ case BluetoothHandsfreeClientCall.CALL_STATE_WAITING:
+ callId.setBackgroundColor(getColor(R.color.call_new));
+ break;
+
+ default:
+ callId.setBackgroundColor(getColor(R.color.call_unknown));
+ break;
+ }
+
+ setIndicator(view, R.id.call_state_active,
+ call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE);
+ setIndicator(view, R.id.call_state_held,
+ call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_HELD);
+ setIndicator(view, R.id.call_state_dialing,
+ call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_DIALING);
+ setIndicator(view, R.id.call_state_alerting,
+ call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_ALERTING);
+ setIndicator(view, R.id.call_state_incoming,
+ call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_INCOMING);
+ setIndicator(view, R.id.call_state_waiting,
+ call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_WAITING);
+ setIndicator(
+ view,
+ R.id.call_state_held_by_rnh,
+ call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD);
+
+ setIndicator(view, R.id.call_multiparty, call.isMultiParty());
+
+ return view;
+ }
+
+ public void add(BluetoothHandsfreeClientCall call) {
+ mCalls.put(call.getId(), call);
+ notifyDataSetChanged();
+ }
+
+ public void remove(BluetoothHandsfreeClientCall call) {
+ if (call.getId() == mSelectedId) {
+ mSelectedId = 0;
+ }
+ mCalls.remove(call.getId());
+ notifyDataSetChanged();
+ }
+
+ public void removeAll() {
+ mCalls.clear();
+ notifyDataSetChanged();
+ }
+
+ public void toggleSelected(int id) {
+ if (id == mSelectedId) {
+ mSelectedId = 0;
+ } else {
+ mSelectedId = id;
+ }
+ notifyDataSetChanged();
+ }
+
+ public BluetoothHandsfreeClientCall getSelected() {
+ return mCalls.get(mSelectedId);
+ }
+
+ public boolean hasCallsInState(int... state) {
+ for (int idx = 0; idx < mCalls.size(); idx++) {
+ BluetoothHandsfreeClientCall call = mCalls.valueAt(idx);
+ for (int s : state) {
+ if (call.getState() == s) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void setIndicator(View view, int id, boolean state) {
+ TextView txt = (TextView) view.findViewById(id);
+ txt.setVisibility(state ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ private int getColor(int id) {
+ return getResources().getColor(id);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.calls_list_fragment, null);
+
+ mCallsList = (ListView) view.findViewById(R.id.calls_list);
+ mCallsList.setAdapter(new CallsAdapter());
+ mCallsList.setOnItemClickListener(this);
+
+ mActionAccept = (Button) view.findViewById(R.id.call_action_accept);
+ mActionHoldAndAccept = (Button) view.findViewById(R.id.call_action_hold_and_accept);
+ mActionReleaseAndAccept = (Button) view.findViewById(R.id.call_action_release_and_accept);
+ mActionReject = (Button) view.findViewById(R.id.call_action_reject);
+ mActionTerminate = (Button) view.findViewById(R.id.call_action_terminate);
+ mActionHold = (Button) view.findViewById(R.id.call_action_hold);
+ mActionRespondAndHold = (Button) view.findViewById(R.id.call_action_respond_and_hold);
+ mActionPrivateMode = (Button) view.findViewById(R.id.call_action_private_mode);
+ mActionExplicitTransfer = (Button) view.findViewById(R.id.call_action_explicit_transfer);
+
+ mActionAccept.setEnabled(false);
+ mActionHoldAndAccept.setEnabled(false);
+ mActionReleaseAndAccept.setEnabled(false);
+ mActionReject.setEnabled(false);
+ mActionTerminate.setEnabled(false);
+ mActionHold.setEnabled(false);
+ mActionRespondAndHold.setEnabled(false);
+ mActionPrivateMode.setEnabled(false);
+ mActionExplicitTransfer.setEnabled(false);
+
+ mActionAccept.setOnClickListener(this);
+ mActionHoldAndAccept.setOnClickListener(this);
+ mActionReleaseAndAccept.setOnClickListener(this);
+ mActionReject.setOnClickListener(this);
+ mActionTerminate.setOnClickListener(this);
+ mActionHold.setOnClickListener(this);
+ mActionRespondAndHold.setOnClickListener(this);
+ mActionPrivateMode.setOnClickListener(this);
+ mActionExplicitTransfer.setOnClickListener(this);
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mActivity = (HfpTestActivity) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(this.getClass().getSimpleName()
+ + " can be only attached to " + HfpTestActivity.class.getSimpleName());
+ }
+ }
+
+ public void onCallChanged(BluetoothHandsfreeClientCall call) {
+ Log.v(TAG, "onCallChanged(): call=" + HfpTestActivity.callToJson(call));
+
+ CallsAdapter adapter = (CallsAdapter) mCallsList.getAdapter();
+
+ if (call.getState() == BluetoothHandsfreeClientCall.CALL_STATE_TERMINATED) {
+ adapter.remove(call);
+ } else {
+ // add will also update existing call with the same id
+ adapter.add(call);
+ }
+
+ updateActionsState();
+ }
+
+ @Override
+ public void onClick(View v) {
+ BluetoothHandsfreeClient cli = mActivity.mBluetoothHandsfreeClient;
+ BluetoothDevice device = mActivity.mDevice;
+ CallsAdapter adapter = (CallsAdapter) mCallsList.getAdapter();
+ BluetoothHandsfreeClientCall selectedCall = adapter.getSelected();
+ boolean result = true;
+
+ switch (v.getId()) {
+ case R.id.call_action_accept:
+ result = cli.acceptCall(device, BluetoothHandsfreeClient.CALL_ACCEPT_NONE);
+ break;
+
+ case R.id.call_action_hold_and_accept:
+ result = cli.acceptCall(device, BluetoothHandsfreeClient.CALL_ACCEPT_HOLD);
+ break;
+
+ case R.id.call_action_release_and_accept:
+ result = cli.acceptCall(device, BluetoothHandsfreeClient.CALL_ACCEPT_TERMINATE);
+ break;
+
+ case R.id.call_action_reject:
+ result = cli.rejectCall(device);
+ break;
+
+ case R.id.call_action_terminate:
+ result = cli.terminateCall(device, selectedCall == null ? 0 : selectedCall.getId());
+ break;
+
+ case R.id.call_action_hold:
+ result = cli.holdCall(device);
+ break;
+
+ case R.id.call_action_respond_and_hold:
+ result = cli.holdCall(device);
+ break;
+
+ case R.id.call_action_private_mode:
+ result = cli.enterPrivateMode(device, selectedCall.getId());
+ break;
+
+ case R.id.call_action_explicit_transfer:
+ result = cli.explicitCallTransfer(device);
+ break;
+ }
+
+ if (!result) {
+ Toast.makeText(getActivity(), "\"" + ((TextView) v).getText() + "\" FAILED",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ((CallsAdapter) parent.getAdapter()).toggleSelected((int) id);
+
+ updateActionsState();
+ }
+
+ public void onConnStateChanged(int state, int prevState) {
+ if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ ((CallsAdapter) mCallsList.getAdapter()).removeAll();
+
+ updateActionsState();
+ }
+ }
+
+ private void updateActionsState() {
+ CallsAdapter adapter = (CallsAdapter) mCallsList.getAdapter();
+ BluetoothHandsfreeClientCall selectedCall = adapter.getSelected();
+
+ boolean canAccept = false;
+ boolean canHoldAndAccept = false;
+ boolean canReleaseAndAccept = false;
+ boolean canReject = false;
+ boolean canTerminate = false;
+ boolean canRespondAndHold = false;
+ boolean canHold = false;
+
+ boolean hasIncoming = adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_INCOMING);
+ boolean hasActive = adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE);
+ boolean hasWaiting = adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_WAITING);
+ boolean hasHeld = adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_HELD,
+ BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD);
+
+ if (hasIncoming) {
+ canAccept = true;
+ canReject = mActivity.mFeatReject;
+ canRespondAndHold = true;
+
+ mActionAccept.setText(R.string.call_action_accept);
+ mActionReject.setText(R.string.call_action_reject);
+ }
+
+ if (hasActive || adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_ALERTING,
+ BluetoothHandsfreeClientCall.CALL_STATE_DIALING)) {
+ canTerminate = true;
+ }
+
+ if (hasHeld) {
+ canAccept = true;
+ canReject = mActivity.mFeatReject;
+ canHoldAndAccept = mActivity.mFeatAcceptHeldOrWaiting;
+ }
+
+ if (hasWaiting) {
+ canReject = mActivity.mFeatReject;
+ mActionAccept.setText(R.string.call_action_accept);
+ mActionReject.setText(R.string.call_action_reject);
+
+ if (hasActive) {
+ canHoldAndAccept = mActivity.mFeatAcceptHeldOrWaiting;
+ canReleaseAndAccept = mActivity.mFeatReleaseAndAccept;
+
+ mActionHoldAndAccept.setText(R.string.call_action_hold_and_accept);
+ } else if (hasHeld) {
+ canAccept = true;
+ canHoldAndAccept = false;
+ canReleaseAndAccept = false;
+ }
+ }
+
+ if (adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE) &&
+ !adapter.hasCallsInState(BluetoothHandsfreeClientCall.CALL_STATE_HELD,
+ BluetoothHandsfreeClientCall.CALL_STATE_DIALING,
+ BluetoothHandsfreeClientCall.CALL_STATE_ALERTING,
+ BluetoothHandsfreeClientCall.CALL_STATE_INCOMING,
+ BluetoothHandsfreeClientCall.CALL_STATE_WAITING,
+ BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD)) {
+ canHold = true;
+ }
+
+ // TODO: this should be perhaps done in some more reasonable way
+ if (hasHeld && !hasWaiting) {
+ if (hasActive) {
+ canAccept = true;
+ canReleaseAndAccept = mActivity.mFeatReleaseAndAccept;
+
+ mActionAccept.setText(R.string.call_action_merge);
+ mActionHoldAndAccept.setText(R.string.call_action_swap);
+ mActionReject.setText(R.string.call_action_reject_held);
+ } else {
+ canAccept = false;
+ mActionAccept.setText(R.string.call_action_accept);
+ mActionHoldAndAccept.setText(R.string.call_action_accept_held);
+ mActionReject.setText(R.string.call_action_reject);
+ // Actions for incoming call when there is already a held one.
+ // We cannot accept the held one now. Instead handle the incoming call first.
+ if (hasIncoming) {
+ canAccept = true;
+ canHoldAndAccept = false;
+ }
+ }
+ } else {
+ mActionHoldAndAccept.setText(R.string.call_action_hold_and_accept);
+ }
+
+ mActionAccept.setEnabled(canAccept);
+ mActionHoldAndAccept.setEnabled(canHoldAndAccept);
+ mActionReleaseAndAccept.setEnabled(canReleaseAndAccept);
+ mActionReject.setEnabled(canReject);
+ mActionTerminate.setEnabled(canTerminate);
+ mActionRespondAndHold.setEnabled(canRespondAndHold);
+
+ mActionHold.setEnabled(canHold);
+
+ mActionPrivateMode.setEnabled(mActivity.mFeatEnhancedCallControl && selectedCall != null && selectedCall.isMultiParty());
+
+ mActionExplicitTransfer.setEnabled(mActivity.mFeatMergeDetach && (adapter.getCount() > 1));
+ }
+}*/
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/DialpadFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/DialpadFragment.java
new file mode 100644
index 0000000..3e5aa15
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/DialpadFragment.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*package org.codeaurora.bttestapp;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.AbsListView.LayoutParams;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.GridView;
+import android.widget.ImageButton;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+
+import com.qualcomm.bttestapp.R;
+
+public class DialpadFragment extends Fragment implements OnClickListener, OnLongClickListener {
+
+ private HfpTestActivity mActivity;
+
+ private GridView mButtonsGrid;
+
+ private EditText mNumberEdit;
+
+ private ToggleButton mDtmfButton;
+
+ private class ButtonAdapter extends BaseAdapter {
+
+ private final String[] mButtons = {
+ "1", "2", "3", "A",
+ "4", "5", "6", "B",
+ "7", "8", "9", "C",
+ "*", "0", "#", "D"
+ };
+
+ private final String[] mButtonsShifted = {
+ " ", " ", " ", " ",
+ " ", " ", " ", " ",
+ " ", " ", " ", " ",
+ " ", "+", " ", " "
+ };
+
+ private final Context mContext;
+
+ ButtonAdapter(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public int getCount() {
+ return mButtons.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return new Pair<String, String>(mButtons[position], mButtonsShifted[position]);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Button btn;
+
+ if (convertView == null) {
+ btn = new Button(mContext);
+ btn.setLayoutParams(new LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
+ btn.setOnClickListener(DialpadFragment.this);
+ btn.setOnLongClickListener(DialpadFragment.this);
+ } else {
+ btn = (Button) convertView;
+ }
+
+ @SuppressWarnings("unchecked")
+ Pair<String, String> item = (Pair<String, String>) getItem(position);
+
+ btn.setText(item.first);
+ btn.setId(R.id.dialpad_button);
+ btn.setTag(item.first);
+ btn.setTag(R.id.dialpad_button_pos, position);
+
+ return btn;
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ Context context = getActivity();
+
+ View view = inflater.inflate(R.layout.dialpad_fragment, null);
+
+ mNumberEdit = (EditText) view.findViewById(R.id.dialpad_number);
+ mNumberEdit.setOnLongClickListener(this);
+
+ mButtonsGrid = (GridView) view.findViewById(R.id.dialpad);
+ mButtonsGrid.setAdapter(new ButtonAdapter(context));
+
+ ((ImageButton) view.findViewById(R.id.dialpad_del)).setOnClickListener(this);
+ ((ImageButton) view.findViewById(R.id.dialpad_del)).setOnLongClickListener(this);
+ ((Button) view.findViewById(R.id.dialpad_dial)).setOnClickListener(this);
+ ((Button) view.findViewById(R.id.dialpad_memdial)).setOnClickListener(this);
+
+ mDtmfButton = (ToggleButton) view.findViewById(R.id.dialpad_dtmf);
+ mDtmfButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ getView().findViewById(R.id.dialpad_number).setEnabled(!isChecked);
+ getView().findViewById(R.id.dialpad_del).setEnabled(!isChecked);
+ getView().findViewById(R.id.dialpad_dial).setEnabled(!isChecked);
+ getView().findViewById(R.id.dialpad_memdial).setEnabled(!isChecked);
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mActivity = (HfpTestActivity) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(this.getClass().getSimpleName()
+ + " can be only attached to " + HfpTestActivity.class.getSimpleName());
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.dialpad_button:
+ int pos = (Integer) view.getTag(R.id.dialpad_button_pos);
+ onClickDialpadButton(pos, false);
+ break;
+
+ case R.id.dialpad_del:
+ onClickDelete();
+ break;
+
+ case R.id.dialpad_dial:
+ onClickDial();
+ break;
+
+ case R.id.dialpad_memdial:
+ onClickMemDial();
+ break;
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ switch (view.getId()) {
+ case R.id.dialpad_button:
+ int pos = (Integer) view.getTag(R.id.dialpad_button_pos);
+ onClickDialpadButton(pos, true);
+ return true;
+
+ case R.id.dialpad_del:
+ mNumberEdit.setText("");
+ return true;
+
+ case R.id.dialpad_number:
+ onClickDialpadNumber();
+ return true;
+ }
+
+ return false;
+ }
+
+ private void onClickDialpadNumber() {
+ ClipboardManager cm = (ClipboardManager) mNumberEdit.getContext()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+
+ String txt = mNumberEdit.getText().toString();
+
+ if (txt.isEmpty()) {
+ ClipData clipData = cm.getPrimaryClip();
+ if (clipData != null) {
+ ClipData.Item item = clipData.getItemAt(0);
+ mNumberEdit.setText(item.getText());
+ }
+ } else {
+ cm.setPrimaryClip(ClipData.newPlainText(txt, txt));
+
+ Toast.makeText(mNumberEdit.getContext(), "copied: " +
+ txt, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void onClickDialpadButton(int position, boolean shift) {
+ @SuppressWarnings("unchecked")
+ Pair<String, String> bval = (Pair<String, String>) mButtonsGrid.getItemAtPosition(position);
+
+ if (mDtmfButton.isChecked()) {
+ mActivity.mBluetoothHandsfreeClient.sendDTMF(mActivity.mDevice,
+ bval.first.getBytes()[0]);
+ } else {
+ if (shift) {
+ mNumberEdit.append(bval.second.trim());
+ } else {
+ mNumberEdit.append(bval.first.trim());
+ }
+ }
+ }
+
+ private void onClickDelete() {
+ String txt = mNumberEdit.getText().toString();
+
+ if (txt.length() > 0) {
+ txt = txt.substring(0, txt.length() - 1);
+ mNumberEdit.setText(txt);
+ }
+ }
+
+ private void onClickDial() {
+ String number = mNumberEdit.getText().toString().trim();
+
+ if (number.isEmpty()) {
+ mActivity.mBluetoothHandsfreeClient.redial(mActivity.mDevice);
+ } else {
+ mActivity.mBluetoothHandsfreeClient.dial(mActivity.mDevice, mNumberEdit.getText()
+ .toString());
+ }
+ }
+
+ private void onClickMemDial() {
+ try {
+ mActivity.mBluetoothHandsfreeClient.dialMemory(mActivity.mDevice,
+ Integer.valueOf(mNumberEdit.getText().toString()));
+ } catch (NumberFormatException e) {
+ // just ignore
+ }
+ }
+
+ public void setNumber(String number) {
+ mNumberEdit.setText(number);
+ }
+}*/
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/FilterActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/FilterActivity.java
new file mode 100644
index 0000000..4c4eb09
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/FilterActivity.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.CheckBox;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public abstract class FilterActivity extends Activity {
+
+ private final String TAG = "FilterActivity";
+
+ protected Map<Integer, Integer> mParameters = new HashMap<Integer, Integer>();
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ long tmp = getIntent().getLongExtra("filter", 0);
+
+ fillParameters();
+
+ for (int i = 0; i < 64; i++) {
+ if ((tmp & 0x01) == 0x01) {
+ Integer id = mParameters.get(i);
+
+ if (id != null) {
+ check(id);
+ }
+ }
+
+ tmp = (tmp >> 1);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.menu_filter, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_save:
+ Intent returnIntent = new Intent();
+ returnIntent.putExtra("result", getSelectedValue());
+ setResult(RESULT_OK, returnIntent);
+ finish();
+ break;
+ default:
+ Logger.w(TAG, "Unknown item selected.");
+ break;
+ }
+ return true;
+ }
+
+ protected void check(int id) {
+ setChecked(id, true);
+ }
+
+ protected Boolean isChecked(int id) {
+ CheckBox checkbox = (CheckBox) findViewById(id);
+ if (checkbox != null) {
+ return checkbox.isChecked();
+ }
+
+ Logger.w(TAG, "Check box " + id + " not found.");
+ return false;
+ }
+
+ protected void setChecked(int id, Boolean check) {
+ CheckBox checkbox = (CheckBox) findViewById(id);
+ if (checkbox != null) {
+ checkbox.setChecked(check);
+ } else {
+ Logger.w(TAG, "Check box " + id + " not found.");
+ }
+ }
+
+ protected long getSelectedValue() {
+ long result = 0;
+
+ Iterator<Entry<Integer, Integer>> it = mParameters.entrySet().iterator();
+
+ while (it.hasNext()) {
+ Entry<Integer, Integer> pair = it.next();
+
+ Integer key = pair.getKey();
+ Integer id = pair.getValue();
+
+ if (isChecked(id)) {
+ result |= ((long) 1 << key);
+ }
+ }
+
+ return result;
+ }
+
+ protected void addParameter(Integer byteNumber, Integer id) {
+ mParameters.put(byteNumber, id);
+ }
+
+ abstract protected void fillParameters();
+
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/GetTextDialogFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/GetTextDialogFragment.java
new file mode 100644
index 0000000..475ef02
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/GetTextDialogFragment.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.widget.EditText;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+
+class GetTextDialogFragment extends DialogFragment {
+
+ private GetTextDialogListener mListener;
+
+ public interface GetTextDialogListener {
+ public void onGetTextDialogPositive(DialogFragment dialog, String text);
+ };
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+
+ builder.setView(inflater.inflate(R.layout.string_picker_dialog, null))
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ EditText edt = (EditText) GetTextDialogFragment.this.getDialog()
+ .findViewById(R.id.text);
+ mListener.onGetTextDialogPositive(GetTextDialogFragment.this, edt.getText()
+ .toString());
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mListener = (GetTextDialogListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement GetTextDialogListener");
+ }
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/HfpTestActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/HfpTestActivity.java
new file mode 100644
index 0000000..d62d5c4
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/HfpTestActivity.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*package org.codeaurora.bttestapp;
+
+import android.app.ActionBar;
+import android.app.DialogFragment;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothHandsfreeClientCall;
+import android.bluetooth.BluetoothProfile;
+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.media.AudioManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.qualcomm.bttestapp.CallHistoryDialogFragment.CallHistoryDialogListener;
+import com.qualcomm.bttestapp.R;
+import com.qualcomm.bttestapp.util.Logger;
+import com.qualcomm.bttestapp.util.MonkeyEvent;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class HfpTestActivity extends MonkeyActivity implements IBluetoothConnectionObserver,
+ CallHistoryDialogListener {
+
+ private final String TAG = "HfpTestActivity";
+
+ private DialpadFragment mDialpadFragment = null;
+
+ private IndicatorsFragment mIndicatorsFragment = null;
+
+ private CallsListFragment mCallsListFragment = null;
+
+ private final ArrayList<String> mCallHistory = new ArrayList<String>();
+
+ private ActionBar mActionBar = null;
+
+ // /* this should be visible for fragments ///
+ BluetoothHandsfreeClient mBluetoothHandsfreeClient;
+ ProfileService mProfileService = null;
+ BluetoothDevice mDevice;
+
+ ///* this should be done in a more elegant way //
+ public boolean mFeatVtag;
+ public boolean mFeatVoiceRecognition;
+ public boolean mFeatThreeWayCalling;
+ public boolean mFeatEnhancedCallControl;
+
+ public boolean mFeatReject;
+ public boolean mFeatMerge;
+ public boolean mFeatMergeDetach;
+ public boolean mFeatReleaseAndAccept;
+ public boolean mFeatAcceptHeldOrWaiting;
+ public boolean mFeatReleaseHeldOrWaiting;
+
+ private final BroadcastReceiver mHfpClientReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ BluetoothDevice device = (BluetoothDevice)
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ if (action.equals(BluetoothHandsfreeClient.ACTION_CONNECTION_STATE_CHANGED)) {
+
+ int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+ onReceiveActionConnectionStateChanged(device, prevState, state, intent.getExtras());
+ mCallsListFragment.onConnStateChanged(state, prevState);
+ mIndicatorsFragment.onConnStateChanged(state, prevState);
+
+ // Send MonkeyEvent
+ new MonkeyEvent("hfp-connection-state-changed", true)
+ .addReplyParam("state", state)
+ .addReplyParam("prevState", prevState)
+ .send();
+ } else if (action.equals(BluetoothHandsfreeClient.ACTION_AG_EVENT)) {
+
+ mIndicatorsFragment.onAgEvent(intent.getExtras());
+
+ } else if (action.equals(BluetoothHandsfreeClient.ACTION_CALL_CHANGED)) {
+
+ BluetoothHandsfreeClientCall call = (BluetoothHandsfreeClientCall) intent.
+ getParcelableExtra(BluetoothHandsfreeClient.EXTRA_CALL);
+ onCallChanged(call);
+ mCallsListFragment.onCallChanged(call);
+
+ // Send MonkeyEvent
+ new MonkeyEvent("hfp-call-changed", true)
+ .addExtReply(callToJson(call))
+ .send();
+ } else if (action.equals(BluetoothHandsfreeClient.ACTION_AUDIO_STATE_CHANGED)) {
+
+ int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+ onReceiveAudioStateChange(device, prevState, state);
+ mIndicatorsFragment.onAudioStateChanged(state, prevState);
+
+ // Send MonkeyEvent
+ new MonkeyEvent("hfp-audio-state-changed", true)
+ .addReplyParam("state", state)
+ .addReplyParam("prevState", prevState)
+ .send();
+ } else if(action.equals(BluetoothHandsfreeClient.ACTION_RESULT)) {
+ int result = intent.getIntExtra(
+ BluetoothHandsfreeClient.EXTRA_RESULT_CODE, -1);
+ int cme = intent.getIntExtra(
+ BluetoothHandsfreeClient.EXTRA_CME_CODE, -1);
+
+ onReceiveResult(result, cme);
+
+ // Send MonkeyEvent
+ new MonkeyEvent("hfp-receive-result", true)
+ .addReplyParam("result", result)
+ .addReplyParam("cme", cme)
+ .send();
+ } else if (action.equals(BluetoothHandsfreeClient.ACTION_LAST_VTAG)) {
+
+ String number = intent.getStringExtra(BluetoothHandsfreeClient.EXTRA_NUMBER);
+ Toast.makeText(HfpTestActivity.this, "Phone number received: " + number,
+ Toast.LENGTH_LONG).show();
+ // Send MonkeyEvent
+ new MonkeyEvent("hfp-receive-phonenumber", true)
+ .addReplyParam("number", number)
+ .send();
+ }
+ }
+
+ private void onCallChanged(BluetoothHandsfreeClientCall call) {
+ int state = call.getState();
+ String number = call.getNumber().trim();
+
+ new MonkeyEvent("hfp-call-changed", true).addExtReply(callToJson(call)).send();
+
+ if (number.isEmpty()) {
+ return;
+ }
+
+ /// we add any "new" call to list and put in at the beginning
+ if (state == BluetoothHandsfreeClientCall.CALL_STATE_ALERTING ||
+ state == BluetoothHandsfreeClientCall.CALL_STATE_DIALING ||
+ state == BluetoothHandsfreeClientCall.CALL_STATE_INCOMING ||
+ state == BluetoothHandsfreeClientCall.CALL_STATE_WAITING) {
+ mCallHistory.remove(number);
+ mCallHistory.add(0, number);
+
+ if (mCallHistory.size() > 20) {
+ mCallHistory.remove(20);
+ }
+ }
+ }
+
+ private void onReceiveResult(int result, int cme) {
+ Logger.v(TAG, "onReceiveResult (result: " + result + " cme: " +
+ cme);
+
+ Toast.makeText(HfpTestActivity.this,
+ resultCodeToString(result, cme),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ private synchronized void onReceiveAudioStateChange(BluetoothDevice device,
+ int prevState, int state) {
+ Logger.v(TAG, "onReceiveAudioStateChange (" + prevState +
+ " -> " + state + ")");
+
+ invalidateOptionsMenu();
+ }
+
+ private void onReceiveActionConnectionStateChanged(BluetoothDevice device,
+ int prevState, int state, Bundle features) {
+ Logger.v(TAG, "onReceiveActionConnectionStateChanged: " +
+ device.getAddress() + " (" +
+ String.valueOf(prevState) + " -> " +
+ String.valueOf(state) + ")");
+
+ switch (state) {
+ case BluetoothProfile.STATE_CONNECTED:
+ updateAgFeatures(features);
+ Toast.makeText(HfpTestActivity.this, R.string.msg_connection_success,
+ Toast.LENGTH_SHORT).show();
+
+ new MonkeyEvent("hfp-connected", true).send();
+
+ break;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ new MonkeyEvent("hfp-disconnected", true).send();
+
+ Toast.makeText(HfpTestActivity.this, R.string.msg_disconnection_success,
+ Toast.LENGTH_SHORT).show();
+
+ break;
+ }
+
+ invalidateOptionsMenu();
+ }
+ };
+
+ ///*
+ * HFP Service Connection.
+ ///
+ private final ServiceConnection mHfpServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Logger.v(TAG, "onServiceConnected()");
+ mProfileService = ((ProfileService.LocalBinder) service).getService();
+ mBluetoothHandsfreeClient = mProfileService.getHfpClient();
+
+ int connState = mBluetoothHandsfreeClient.getConnectionState(mDevice);
+
+ if (connState == BluetoothProfile.STATE_CONNECTED) {
+ // trigger refresh of calls list
+ for (BluetoothHandsfreeClientCall call : mBluetoothHandsfreeClient
+ .getCurrentCalls(mDevice)) {
+ mCallsListFragment.onCallChanged(call);
+ }
+
+ // get supported AG features
+ updateAgFeatures(mBluetoothHandsfreeClient.getCurrentAgFeatures(mDevice));
+ }
+
+ // trigger refresh of indicators
+ mIndicatorsFragment.onAudioStateChanged(
+ mBluetoothHandsfreeClient.getAudioState(mDevice), 0);
+ mIndicatorsFragment.onConnStateChanged(connState, 0);
+ mIndicatorsFragment.onAgEvent(mBluetoothHandsfreeClient.getCurrentAgEvents(mDevice));
+
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Logger.v(TAG, "onServiceDisconnected()");
+ mProfileService = null;
+ mBluetoothHandsfreeClient = null;
+
+ invalidateOptionsMenu();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Logger.v(TAG, "onCreate()");
+
+ ActivityHelper.initialize(this, R.layout.activity_hfp_test);
+ BluetoothConnectionReceiver.registerObserver(this);
+
+ // bind to app service
+ Intent intent = new Intent(this, ProfileService.class);
+ bindService(intent, mHfpServiceConnection, BIND_AUTO_CREATE);
+
+ prepareActionBar();
+
+ mIndicatorsFragment = (IndicatorsFragment) getFragmentManager().findFragmentById(
+ R.id.indicators);
+ mCallsListFragment = (CallsListFragment) getFragmentManager().findFragmentById(
+ R.id.calls_list);
+ mDialpadFragment = (DialpadFragment) getFragmentManager().findFragmentById(R.id.dialpad);
+
+ setVolumeControlStream(AudioManager.STREAM_BLUETOOTH_SCO);
+ }
+
+ @Override
+ protected void onDestroy() {
+ Logger.v(TAG, "onDestroy");
+
+ super.onDestroy();
+
+ unbindService(mHfpServiceConnection);
+ BluetoothConnectionReceiver.removeObserver(this);
+ }
+
+ @Override
+ protected void onResume() {
+ Logger.v(TAG, "onResume");
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothHandsfreeClient.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothHandsfreeClient.ACTION_AG_EVENT);
+ filter.addAction(BluetoothHandsfreeClient.ACTION_CALL_CHANGED);
+ filter.addAction(BluetoothHandsfreeClient.ACTION_AUDIO_STATE_CHANGED);
+ filter.addAction(BluetoothHandsfreeClient.ACTION_RESULT);
+ filter.addAction(BluetoothHandsfreeClient.ACTION_LAST_VTAG);
+ registerReceiver(mHfpClientReceiver, filter);
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ Logger.v(TAG, "onPause");
+
+ unregisterReceiver(mHfpClientReceiver);
+ super.onPause();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ mActionBarMenu = menu;
+
+ getMenuInflater().inflate(R.menu.menu_hfp_test, menu);
+ menu.findItem(R.id.menu_request_phone_num).setEnabled(mFeatVtag);
+
+ return true;
+ }
+
+ @Override
+ public void onDeviceChanged(BluetoothDevice device) {
+ Logger.v(TAG, "onDeviceChanged()");
+
+ mDevice = device;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_request_phone_num:
+ mBluetoothHandsfreeClient.getLastVoiceTagNumber(mDevice);
+ return true;
+
+ case R.id.menu_call_history:
+ new CallHistoryDialogFragment(mCallHistory).show(getFragmentManager(),
+ "call-history");
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onDeviceDisconected() {
+ Logger.v(TAG, "onDeviceDisconected");
+
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onCallHistoryDialogPositive(DialogFragment dialog, String number) {
+ mDialpadFragment.setNumber(number);
+ }
+
+ private void prepareActionBar() {
+ Logger.v(TAG, "prepareActionBar()");
+
+ mActionBar = getActionBar();
+ if (mActionBar != null) {
+ mActionBar.setTitle(R.string.title_hfp_test);
+ mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ }
+ }
+
+ private String resultCodeToString(int result, int cme) {
+ Logger.v(TAG, "resultCodeToString()");
+
+ switch (result) {
+ case BluetoothHandsfreeClient.ACTION_RESULT_OK:
+ return getString(R.string.hfptest_result_ok_text);
+ case BluetoothHandsfreeClient.ACTION_RESULT_ERROR:
+ return getString(R.string.hfptest_result_error_text);
+ case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_NO_CARRIER:
+ return getString(R.string.hfptest_result_no_carrier_text);
+ case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_BUSY:
+ return getString(R.string.hfptest_result_busy_text);
+ case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_NO_ANSWER:
+ return getString(R.string.hfptest_result_no_answer_text);
+ case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_DELAYED:
+ return getString(R.string.hfptest_result_delayed_text);
+ case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_BLACKLISTED:
+ return getString(R.string.hfptest_result_blacklisted_text);
+ case BluetoothHandsfreeClient.ACTION_RESULT_ERROR_CME:
+ return getString(R.string.hfptest_result_cme_text) + " (" + cme + ")";
+ }
+
+ return getString(R.string.hfptest_call_state_unknown_text);
+ }
+
+ @Override
+ protected void onMonkeyQuery(String op, Bundle params) {
+ if ("getIndicators".equals(op)) {
+ Bundle bundle = mBluetoothHandsfreeClient.getCurrentAgEvents(mDevice);
+
+ MonkeyEvent me = new MonkeyEvent("hfp-ag-event", true);
+
+ if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_VOICE_RECOGNITION)) {
+ me.addReplyParam("vr", bundle.getInt(BluetoothHandsfreeClient.EXTRA_VOICE_RECOGNITION));
+ }
+
+ if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_IN_BAND_RING)) {
+ me.addReplyParam("in-band", bundle.getInt(BluetoothHandsfreeClient.EXTRA_IN_BAND_RING));
+ }
+
+ if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_NETWORK_STATUS)) {
+ me.addReplyParam("network-status",
+ bundle.getInt(BluetoothHandsfreeClient.EXTRA_NETWORK_STATUS));
+ }
+
+ if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_NETWORK_ROAMING)) {
+ me.addReplyParam("network-roaming",
+ bundle.getInt(BluetoothHandsfreeClient.EXTRA_NETWORK_ROAMING));
+ }
+
+ if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_NETWORK_SIGNAL_STRENGTH)) {
+ me.addReplyParam("signal-strength",
+ bundle.getInt(BluetoothHandsfreeClient.EXTRA_NETWORK_SIGNAL_STRENGTH));
+ }
+
+ if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_BATTERY_LEVEL)) {
+ me.addReplyParam("battery-level",
+ bundle.getInt(BluetoothHandsfreeClient.EXTRA_BATTERY_LEVEL));
+ }
+
+ if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_OPERATOR_NAME)) {
+ me.addReplyParam("operator-name",
+ bundle.getString(BluetoothHandsfreeClient.EXTRA_OPERATOR_NAME));
+ }
+
+ if (bundle.containsKey(BluetoothHandsfreeClient.EXTRA_SUBSCRIBER_INFO)) {
+ me.addReplyParam("subscriber-info",
+ bundle.getString(BluetoothHandsfreeClient.EXTRA_SUBSCRIBER_INFO));
+ }
+
+ me.send();
+ }
+ }
+
+ static String callToJson(BluetoothHandsfreeClientCall call) {
+ JSONObject json = new JSONObject();
+
+ try {
+ json.put("id", call.getId());
+ json.put("number", call.getNumber());
+ json.put("state", callStateToString(call.getState()));
+ json.put("outgoing", call.isOutgoing());
+ json.put("multiparty", call.isMultiParty());
+ } catch (JSONException e) {
+ // do nothing
+ }
+
+ return json.toString();
+ }
+
+ static String callStateToString(int state) {
+ switch (state) {
+ case BluetoothHandsfreeClientCall.CALL_STATE_ACTIVE:
+ return "active";
+ case BluetoothHandsfreeClientCall.CALL_STATE_HELD:
+ return "held";
+ case BluetoothHandsfreeClientCall.CALL_STATE_DIALING:
+ return "dialing";
+ case BluetoothHandsfreeClientCall.CALL_STATE_ALERTING:
+ return "alerting";
+ case BluetoothHandsfreeClientCall.CALL_STATE_INCOMING:
+ return "incoming";
+ case BluetoothHandsfreeClientCall.CALL_STATE_WAITING:
+ return "waiting";
+ case BluetoothHandsfreeClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
+ return "held_by_rnh";
+ case BluetoothHandsfreeClientCall.CALL_STATE_TERMINATED:
+ return "terminated";
+ }
+
+ return "unknown";
+ }
+
+ private void updateAgFeatures(Bundle b) {
+ // only supported AG features are being sent
+ mFeatVtag = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT);
+ mFeatVoiceRecognition = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION);
+ mFeatThreeWayCalling = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_3WAY_CALLING);
+ mFeatEnhancedCallControl = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_ECC);
+ mFeatReject = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_REJECT_CALL);
+ mFeatMerge = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_MERGE);
+ mFeatMergeDetach = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH);
+ mFeatReleaseAndAccept = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT);
+ mFeatAcceptHeldOrWaiting = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL);
+ mFeatReleaseHeldOrWaiting = b.containsKey(BluetoothHandsfreeClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL);
+ }
+}*/
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IBluetoothConnectionObserver.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IBluetoothConnectionObserver.java
new file mode 100644
index 0000000..dd1df50
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IBluetoothConnectionObserver.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.bluetooth.BluetoothDevice;
+
+public interface IBluetoothConnectionObserver {
+ public void onDeviceChanged(BluetoothDevice device);
+
+ public void onDeviceDisconected();
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IndicatorsFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IndicatorsFragment.java
new file mode 100644
index 0000000..af451b0
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/IndicatorsFragment.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*package org.codeaurora.bttestapp;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothProfile;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.qualcomm.bttestapp.R;
+*/
+/*public class IndicatorsFragment extends Fragment implements OnClickListener {
+
+ private HfpTestActivity mActivity;
+
+ private ToggleButton mIndConnState;
+
+ private ToggleButton mIndAudioState;
+
+ private ToggleButton mIndVrState;
+
+ private TextView mIndNetworkState;
+
+ private TextView mIndRoamingState;
+
+ private TextView mIndInbandState;
+
+ private TextView mIndSignalLevel;
+
+ private TextView mIndBatteryLevel;
+
+ private TextView mIndOperator;
+
+ private TextView mIndSubscriber;
+
+ private int mDefaultColor;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.indicators_fragment, null);
+
+ mIndConnState = (ToggleButton) view.findViewById(R.id.ind_conn_state);
+ mIndConnState.setOnClickListener(this);
+ mIndAudioState = (ToggleButton) view.findViewById(R.id.ind_audio_state);
+ mIndAudioState.setOnClickListener(this);
+ mIndVrState = (ToggleButton) view.findViewById(R.id.ind_vr_state);
+ mIndVrState.setOnClickListener(this);
+
+ mIndNetworkState = (TextView) view.findViewById(R.id.ind_network_state);
+ mIndRoamingState = (TextView) view.findViewById(R.id.ind_roaming_state);
+ mIndInbandState = (TextView) view.findViewById(R.id.ind_inband_state);
+
+ mIndSignalLevel = (TextView) view.findViewById(R.id.ind_signal_level);
+ mIndBatteryLevel = (TextView) view.findViewById(R.id.ind_battery_level);
+
+ mIndOperator = (TextView) view.findViewById(R.id.ind_operator);
+ mIndSubscriber = (TextView) view.findViewById(R.id.ind_subscriber);
+
+ mDefaultColor = mIndOperator.getTextColors().getDefaultColor();
+
+ resetIndicators(true);
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mActivity = (HfpTestActivity) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(this.getClass().getSimpleName()
+ + " can be only attached to " + HfpTestActivity.class.getSimpleName());
+ }
+ }
+
+ public void onConnStateChanged(int state, int prevState) {
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ resetIndicators(false);
+ mIndConnState.setChecked(false);
+ mIndConnState.setEnabled(true);
+
+ mIndAudioState.setEnabled(false);
+ mIndVrState.setEnabled(false);
+ mIndVrState.setChecked(false);
+ break;
+
+ case BluetoothProfile.STATE_CONNECTING:
+ mIndConnState.setChecked(true);
+ mIndConnState.setEnabled(false);
+ break;
+
+ case BluetoothProfile.STATE_DISCONNECTING:
+ mIndConnState.setChecked(false);
+ mIndConnState.setEnabled(false);
+ break;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ mIndConnState.setChecked(true);
+ mIndConnState.setEnabled(true);
+
+ mIndAudioState.setEnabled(true);
+ mIndVrState.setEnabled(mActivity.mFeatVoiceRecognition);
+
+ resetIndicators(false);
+ break;
+ }
+ }
+
+ public void onAudioStateChanged(int state, int prevState) {
+ switch (state) {
+ case BluetoothHandsfreeClient.STATE_AUDIO_DISCONNECTED:
+ mIndAudioState.setChecked(false);
+ mIndAudioState.setEnabled(true);
+ break;
+
+ case BluetoothHandsfreeClient.STATE_AUDIO_CONNECTING:
+ mIndAudioState.setChecked(true);
+ mIndAudioState.setEnabled(false);
+ break;
+
+ case BluetoothHandsfreeClient.STATE_AUDIO_CONNECTED:
+ mIndAudioState.setChecked(true);
+ mIndAudioState.setEnabled(true);
+ break;
+ }
+ }
+
+ public void onAgEvent(Bundle params) {
+ if (params == null) {
+ // it can happen when querying for indicators after connection and
+ // it's ok to ignore
+ return;
+ }
+
+ for (String param : params.keySet()) {
+
+ TextView colorInd = null;
+ // TextView valueInd = null;
+ TextView barInd = null;
+
+ if (param.equals(BluetoothHandsfreeClient.EXTRA_VOICE_RECOGNITION)) {
+
+ boolean enabled = (params.getInt(param) != 0);
+ mIndVrState.setChecked(enabled);
+ mIndVrState.setEnabled(mActivity.mFeatVoiceRecognition);
+
+ } else if (param.equals(BluetoothHandsfreeClient.EXTRA_IN_BAND_RING)) {
+
+ colorInd = mIndInbandState;
+
+ } else if (param.equals(BluetoothHandsfreeClient.EXTRA_OPERATOR_NAME)) {
+
+ setOperator(params.getString(param));
+
+ } else if (param.equals(BluetoothHandsfreeClient.EXTRA_NETWORK_STATUS)) {
+
+ colorInd = mIndNetworkState;
+
+ } else if (param.equals(BluetoothHandsfreeClient.EXTRA_NETWORK_ROAMING)) {
+
+ colorInd = mIndRoamingState;
+
+ } else if (param.equals(BluetoothHandsfreeClient.EXTRA_NETWORK_SIGNAL_STRENGTH)) {
+
+ barInd = mIndSignalLevel;
+
+ } else if (param.equals(BluetoothHandsfreeClient.EXTRA_BATTERY_LEVEL)) {
+
+ barInd = mIndBatteryLevel;
+
+ } else if (param.equals(BluetoothHandsfreeClient.EXTRA_SUBSCRIBER_INFO)) {
+
+ setSubscriber(params.getString(param));
+
+ }
+
+ if (colorInd != null) {
+ if (params.getInt(param, -1) != -1) {
+ if (params.getInt(param) != 0) {
+ colorInd.setBackgroundColor(getColor(R.color.ind_on));
+ } else {
+ colorInd.setBackgroundColor(getColor(R.color.ind_off));
+ }
+ colorInd.setVisibility(View.VISIBLE);
+ } else {
+ // ignore
+ }
+ }
+
+ // replaced by barInd, but can be used in future
+ // if (valueInd != null) {
+ // if (params.getInt(param, -1) != -1) {
+ // valueInd.setText(Integer.toString(params.getInt(param)));
+ // } else {
+ // valueInd.setText("");
+ // }
+ // }
+
+ if (barInd != null) {
+ int val = params.getInt(param);
+ int color;
+
+ if (val < 2) {
+ color = getColor(R.color.ind_bar_low);
+ } else if (val < 4) {
+ color = getColor(R.color.ind_bar_medium);
+ } else {
+ color = getColor(R.color.ind_bar_full);
+ }
+
+ SpannableString ss = new SpannableString(getResources().getString(R.string.ind_bar));
+ ss.setSpan(new ForegroundColorSpan(color), 0, val, 0);
+ ss.setSpan(new ForegroundColorSpan(getColor(R.color.ind_bar_off)), val,
+ ss.length(), 0);
+
+ barInd.setText(ss);
+ }
+ }
+ }
+
+ private int getColor(int id) {
+ return getResources().getColor(id);
+ }
+
+ private void resetIndicators(boolean all) {
+ if (all) {
+ mIndConnState.setChecked(false);
+ mIndAudioState.setChecked(false);
+ mIndVrState.setChecked(false);
+
+ mIndConnState.setEnabled(true);
+ mIndAudioState.setEnabled(false);
+ mIndVrState.setEnabled(false);
+ }
+
+ mIndNetworkState.setBackgroundColor(getColor(R.color.ind_off));
+ mIndRoamingState.setBackgroundColor(getColor(R.color.ind_off));
+ mIndInbandState.setBackgroundColor(getColor(R.color.ind_off));
+
+ mIndSignalLevel.setText("");
+ mIndBatteryLevel.setText("");
+
+ setOperator(null);
+ setSubscriber(null);
+ }
+
+ private void setOperator(String text) {
+ if (text != null) {
+ mIndOperator.setText(text);
+ mIndOperator.setTextColor(mDefaultColor);
+ } else {
+ mIndOperator.setText(R.string.ind_operator_unknown);
+ mIndOperator.setTextColor(getColor(R.color.ind_text_unknown));
+ }
+ }
+
+ private void setSubscriber(String text) {
+ if (text != null) {
+ mIndSubscriber.setText(text);
+ mIndSubscriber.setTextColor(mDefaultColor);
+ } else {
+ mIndSubscriber.setText(R.string.ind_subscriber_unknown);
+ mIndSubscriber.setTextColor(getColor(R.color.ind_text_unknown));
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ ///*
+ //* checked state of CompoundButton is toggled automatically when clicked
+ //* so since we need before-click state, it's always opposite of current
+ //* state
+ ///
+ boolean state = !((CompoundButton) view).isChecked();
+
+ switch (view.getId()) {
+ case R.id.ind_conn_state:
+ onClickConnState(state);
+ break;
+
+ case R.id.ind_audio_state:
+ onClickAudioState(state);
+ break;
+
+ case R.id.ind_vr_state:
+ onClickVrState(state);
+ break;
+ }
+ }
+
+ public void onClickConnState(boolean state) {
+ if (state) {
+ mActivity.mBluetoothHandsfreeClient.disconnect(mActivity.mDevice);
+ } else {
+ mActivity.mBluetoothHandsfreeClient.connect(mActivity.mDevice);
+ }
+ }
+
+ public void onClickAudioState(boolean state) {
+ if (state) {
+ mActivity.mBluetoothHandsfreeClient.disconnectAudio();
+ } else {
+ mActivity.mBluetoothHandsfreeClient.connectAudio();
+ }
+ }
+
+ public void onClickVrState(boolean state) {
+ if (state) {
+ mActivity.mBluetoothHandsfreeClient.stopVoiceRecognition(mActivity.mDevice);
+ } else {
+ mActivity.mBluetoothHandsfreeClient.startVoiceRecognition(mActivity.mDevice);
+ }
+ mIndVrState.setEnabled(false);
+ }
+}*/
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MainActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MainActivity.java
new file mode 100644
index 0000000..1a8c85f
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MainActivity.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMasInstance;
+import android.bluetooth.BluetoothUuid;
+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.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+import java.util.ArrayList;
+
+public class MainActivity extends MonkeyActivity {
+
+ private final String TAG = "MainActivity";
+
+ public static final String PREF_DEVICE = "device";
+ public static final String PREF_SERVICES = "services";
+
+ public static final String PICKER_ACTION = "android.bluetooth.devicepicker.action.LAUNCH";
+ public static final String PICKER_SELECTED = "android.bluetooth.devicepicker.action.DEVICE_SELECTED";
+
+ BluetoothDevice mDevice;
+
+ ProfileService mProfileService;
+
+ private boolean mIsBound = false;
+
+ private boolean mDiscoveryInProgress = false;
+
+ private ServicesFragment mServicesFragment = null;
+
+ 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 (PICKER_SELECTED.equals(action)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ updateDevice(device);
+ unregisterReceiver(this);
+
+ mServicesFragment.removeService(null);
+ mServicesFragment.persistServices();
+ }
+ }
+ };
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ Log.v(TAG, "mReceiver got " + action);
+
+ if (BluetoothDevice.ACTION_UUID.equals(action)) {
+ BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (!dev.equals(mDevice)) {
+ return;
+ }
+
+ Parcelable uuids[] = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
+
+ if (uuids != null) {
+ for (Parcelable uuid : uuids) {
+ if (BluetoothUuid.PBAP_PSE.equals(uuid)) {
+ mServicesFragment.addService(ServicesFragment.Service.Type.PBAP, null);
+ } /*else if (BluetoothUuid.Handsfree_AG.equals(uuid)) {
+ mServicesFragment.addService(ServicesFragment.Service.Type.HFP, null);
+ }*/
+ }
+ }
+
+ mServicesFragment.persistServices();
+
+ if (mDiscoveryInProgress) {
+ Log.v(TAG, "fetching MAS instances");
+ mDevice.fetchMasInstances();
+ }
+
+ } else if (BluetoothDevice.ACTION_MAS_INSTANCE.equals(action)) {
+ BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (!dev.equals(mDevice)) {
+ return;
+ }
+
+ ArrayList<BluetoothMasInstance> insts = intent
+ .getParcelableArrayListExtra(BluetoothDevice.EXTRA_MAS_INSTANCE);
+
+ if (insts != null) {
+ mProfileService.setMasInstances(insts);
+
+ for (BluetoothMasInstance inst : insts) {
+ mServicesFragment.addService(ServicesFragment.Service.Type.MAP, inst);
+ }
+ }
+
+ mServicesFragment.persistServices();
+
+ mDiscoveryInProgress = false;
+ }
+ }
+ };
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mProfileService = ((ProfileService.LocalBinder) service).getService();
+ mProfileService.setDevice(mDevice);
+ mServicesFragment.restoreServices();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mProfileService = null;
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Log.v(TAG, "onCreate");
+
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+ setContentView(R.layout.activity_main);
+
+ mServicesFragment = (ServicesFragment) getFragmentManager().findFragmentById(
+ R.id.services_list);
+
+ Intent intent = new Intent(this, ProfileService.class);
+ startService(intent);
+
+ if (bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ mIsBound = true;
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ Log.v(TAG, "onStart");
+
+ String addr = getPreferences(MODE_PRIVATE).getString(PREF_DEVICE, null);
+
+ try {
+ BluetoothDevice dev = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(addr);
+ updateDevice(dev);
+ } catch (IllegalArgumentException e) {
+ // just leave device unset
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ Log.v(TAG, "onResume");
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothDevice.ACTION_UUID);
+ filter.addAction(BluetoothDevice.ACTION_MAS_INSTANCE);
+ registerReceiver(mReceiver, filter);
+
+ if (BluetoothAdapter.getDefaultAdapter().isEnabled() == false) {
+ Button b = (Button) findViewById(R.id.discover_services);
+ b.setEnabled(false);
+
+ b = (Button) findViewById(R.id.select_device);
+ b.setEnabled(false);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ Log.v(TAG, "onPause");
+
+ unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ Log.v(TAG, "onStop");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ if (mIsBound) {
+ unbindService(mConnection);
+ }
+
+ Log.v(TAG, "onDestroy");
+ }
+
+ public void onButtonClick(View v) {
+ if (v.getId() == R.id.select_device) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(PICKER_SELECTED);
+ registerReceiver(mPickerReceiver, filter);
+
+ Intent intent = new Intent(PICKER_ACTION);
+ intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ startActivity(intent);
+
+ } else if (v.getId() == R.id.discover_services) {
+ if (mDevice == null || mDiscoveryInProgress) {
+ return;
+ }
+
+ mServicesFragment.removeService(null);
+
+ Log.v(TAG, "fetching UUIDs");
+ mDiscoveryInProgress = mDevice.fetchUuidsWithSdp();
+ }
+ }
+
+ private void updateDevice(BluetoothDevice device) {
+ SharedPreferences.Editor prefs = getPreferences(MODE_PRIVATE).edit();
+
+ TextView name = (TextView) findViewById(R.id.device_name);
+ TextView addr = (TextView) findViewById(R.id.device_address);
+
+ if (device == null) {
+ name.setText(R.string.blank);
+ addr.setText(R.string.blank);
+
+ prefs.remove(PREF_DEVICE);
+ } else {
+ Intent intent = new Intent(BluetoothConnectionReceiver.ACTION_NEW_BLUETOOTH_DEVICE);
+ intent.putExtra(BluetoothConnectionReceiver.EXTRA_DEVICE_ADDRESS, device.getAddress());
+ sendBroadcast(intent);
+
+ name.setText(device.getName());
+ addr.setText(device.getAddress());
+
+ prefs.putString(PREF_DEVICE, device.getAddress());
+ }
+
+ prefs.commit();
+
+ mDevice = device;
+
+ if (mProfileService != null) {
+ mProfileService.setDevice(mDevice);
+ }
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java
new file mode 100644
index 0000000..1aa5165
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java
@@ -0,0 +1,1446 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentTransaction;
+import android.bluetooth.BluetoothMasInstance;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.DatePicker;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.TimePicker;
+import android.widget.Toast;
+import android.widget.ViewFlipper;
+
+import com.android.vcard.VCardConstants;
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardProperty;
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapBmessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapEventReport;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapMessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient;
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient.CharsetType;
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient.MessagesFilter;
+import org.codeaurora.bluetooth.bttestapp.GetTextDialogFragment.GetTextDialogListener;
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.StringListDialogFragment.StringListDialogListener;
+import org.codeaurora.bluetooth.bttestapp.services.IMapServiceCallback;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+import org.codeaurora.bluetooth.bttestapp.util.MonkeyEvent;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+public class MapTestActivity extends MonkeyActivity implements GetTextDialogListener,
+ StringListDialogListener {
+
+ private final String TAG = "MapTestActivity";
+
+ private final static short MAX_LIST_COUNT_DEFAULT = 0;
+ private final static short LIST_START_OFFSET_DEFAULT = 0;
+ private final static byte SUBJECT_LENGTH_DEFAULT = 0;
+ private final static String MESSAGES_FILTER_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+ private static final int REQUEST_CODE_GET_PARAMETERS = 0;
+
+ private final String TAB_LIST = "List";
+ private final String TAB_PREVIEW = "Preview";
+ private final String TAB_EDIT = "Edit";
+
+ private int mMasInstanceId = -1;
+
+ private String mCurrentTab = TAB_LIST;
+
+ private final String[] mActionBarTabsNames = {
+ TAB_LIST, TAB_PREVIEW, TAB_EDIT
+ };
+
+ enum Job {
+ IDLE,
+ CONNECT,
+ DISCONNECT,
+ UPDATE_INBOX,
+ SET_PATH,
+ GET_MESSAGE_LISTING,
+ GET_MESSAGE_LISTING_SIZE,
+ GET_FOLDER_LISTING,
+ GET_FOLDER_LISTING_SIZE,
+ GET_MESSAGE,
+ SET_STATUS_READ,
+ SET_STATUS_UNREAD,
+ DELETE_MESSAGE,
+ PUSH_MESSAGE;
+ }
+
+ private Job mCurrentJob = Job.IDLE;
+
+ private String mStartingGetMessageHandle = null;
+
+ private String mPendingGetMessageHandle = null;
+
+ private ArrayDeque<String> mSetPathQueue = null;
+
+ private int mMessageListingParameters = 0;
+
+ private ActionBar mActionBar = null;
+
+ private ViewFlipper mViewFlipper = null;
+
+ private String mPreviewMsgHandle = null;
+ private BluetoothMapBmessage mMapBmessage = null;
+
+ // UI on List tab
+ private TextView mTextViewCurrentFolder = null;
+ private Spinner mSpinnerFolders = null;
+ private ArrayAdapter<String> mAdapterFolders = null;
+ private EditText mEditTextMaxListCountFolders = null;
+ private EditText mEditTextListStartOffsetFolders = null;
+ private EditText mEditTextMaxListCountMessages = null;
+ private EditText mEditTextListStartOffsetMessages = null;
+ private ListView mListViewMessages = null;
+ private EditText mEditTextSubjectLength = null;
+
+ private List<BluetoothMapMessage> mModelMessages = null;
+ private BluetoothMapMessageAdapter mAdapterMessages = null;
+
+ // UI on Preview/Edit tab
+ private EditText mViewBmsgHandle = null;
+ private EditText mViewBmsgStatus = null;
+ private EditText mViewBmsgType = null;
+ private EditText mViewBmsgFolder = null;
+ private EditText mViewBmsgEncoding = null;
+ private EditText mViewBmsgCharset = null;
+ private EditText mViewBmsgLanguage = null;
+ private EditText mViewBmsgContents = null;
+ private EditText mViewBmsgOrig = null;
+ private EditText mViewBmsgRcpt = null;
+
+ // MessagesFilter parameters
+ private byte mMessageType = MessagesFilter.MESSAGE_TYPE_ALL;
+ private Date mPeriodBegin = null;
+ private Date mPeriodEnd = null;
+ private byte mReadStatus = MessagesFilter.READ_STATUS_ANY;
+ private String mRecipient = null;
+ private String mOriginator = null;
+ private byte mPriority = MessagesFilter.PRIORITY_ANY;
+
+ private ArrayList<View> mListTouchables = null;
+
+ private final SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(MESSAGES_FILTER_DATE_FORMAT);
+
+ private ProfileService mProfileService = null;
+
+ private final ArrayList<String> mEditOriginators = new ArrayList<String>();
+
+ private final ArrayList<String> mEditRecipients = new ArrayList<String>();
+
+ private final ServiceConnection mMapServiceConnection = new ServiceConnection() {
+
+ private void shortToast(String s) {
+ Toast.makeText(MapTestActivity.this, s, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mProfileService = ((ProfileService.LocalBinder) service).getService();
+
+ BluetoothMasInstance inst = mProfileService.getMapClient(mMasInstanceId)
+ .getInstanceData();
+ MapTestActivity.this.getActionBar().setSubtitle(inst.getName());
+
+ mProfileService.setMapCallback(mMasInstanceId, new IMapServiceCallback() {
+
+ @Override
+ public void onConnect() {
+ goToState(Job.IDLE);
+ shortToast("MAS connect OK");
+ }
+
+ @Override
+ public void onConnectError() {
+ goToState(Job.IDLE);
+ shortToast("MAS connect FAILED");
+ }
+
+ @Override
+ public void onUpdateInbox() {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-updateinbox", true).send();
+ shortToast("UpdateInbox OK");
+ }
+
+ @Override
+ public void onUpdateInboxError() {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-updateinbox", false).send();
+ shortToast("UpdateInbox FAILED");
+ }
+
+ @Override
+ public void onSetPath(String path) {
+ if (mSetPathQueue != null && mSetPathQueue.size() > 0) {
+ String next = mSetPathQueue.removeFirst();
+ mProfileService.getMapClient(mMasInstanceId).setFolderDown(next);
+ } else {
+ mTextViewCurrentFolder.setText(path);
+ clearFolderList();
+
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-setpath", true).addReplyParam("path", path)
+ .send();
+ shortToast("SetPath OK: path=" + path);
+ }
+ }
+
+ @Override
+ public void onSetPathError(String path) {
+ if (mSetPathQueue != null) {
+ mSetPathQueue = null;
+ mTextViewCurrentFolder.setText(path);
+ clearFolderList();
+ }
+
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-setpath", false).addReplyParam("path", path)
+ .send();
+ shortToast("SetPath FAILED: path=" + path);
+ }
+
+ @Override
+ public void onGetMessagesListing(ArrayList<BluetoothMapMessage> messages) {
+ mAdapterMessages.clear();
+ mAdapterMessages.addAll(messages);
+
+ updateListEmptyView(false);
+
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-messageslisting", true)
+ .addReplyParam("size", messages.size()).addExtReply(messages).send();
+ shortToast("GetMessagesListing OK: size=" + messages.size());
+ }
+
+ @Override
+ public void onGetMessagesListingError() {
+ updateListEmptyView(false);
+
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-messageslisting", false).send();
+ shortToast("GetMessagesListing FAILED");
+ }
+
+ @Override
+ public void onGetFolderListingSize(int size) {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-getfolderlisting-size", true)
+ .addReplyParam("size", size)
+ .send();
+ shortToast("GetMessagesListing size OK: size=" + size);
+ }
+
+ @Override
+ public void onGetFolderListingSizeError() {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-getfolderlisting-size", false).send();
+ shortToast("GetMessagesListing size FAILED");
+ }
+
+ @Override
+ public void onGetFolderListing(ArrayList<String> folders) {
+ clearFolderList();
+ mAdapterFolders.addAll(folders);
+
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-getfolderlisting", true)
+ .addReplyParam("size", folders.size()).addExtReply(folders).send();
+ shortToast("GetFolderListing OK: size=" + folders.size());
+ }
+
+ @Override
+ public void onGetFolderListingError() {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-getfolderlisting", false).send();
+ shortToast("GetFolderListing FAILED");
+ }
+
+ @Override
+ public void onGetMessage(BluetoothMapBmessage message) {
+ updateMessage(message);
+
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-getmessage", true)
+ .addExtReply(message.toString()).send();
+ shortToast("GetMessage OK");
+ }
+
+ @Override
+ public void onGetMessageError() {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-getmessage", false).send();
+ shortToast("GetMessage FAILED");
+ }
+
+ @Override
+ public void onSetMessageStatus() {
+ switch (mCurrentJob) {
+ case SET_STATUS_READ:
+ /* TODO: re-read message? check spec */
+ break;
+ case SET_STATUS_UNREAD:
+ /* TODO: re-read message? check spec */
+ break;
+ case DELETE_MESSAGE:
+ resetPreviewEditUi();
+ Toast.makeText(MapTestActivity.this, "Message deleted!",
+ Toast.LENGTH_SHORT).show();
+ break;
+ default:
+ break;
+ }
+
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-setmessagestatus", true).send();
+ shortToast("SetMessageStatus OK");
+ }
+
+ @Override
+ public void onSetMessageStatusError() {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-setmessagestatus", false).send();
+ shortToast("SetMessageStatus FAILED");
+ }
+
+ @Override
+ public void onPushMessage(String handle) {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-pushmessage", true).addReplyParam("handle", handle)
+ .send();
+ shortToast("PushMessage OK: handle=" + handle);
+ }
+
+ @Override
+ public void onPushMessageError() {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-pushmessage", false).send();
+ shortToast("PushMessage FAILED");
+ }
+
+ @Override
+ public void onGetMessagesListingSize(int size) {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-getmessageslisting-size", true)
+ .addReplyParam("size", size).send();
+ shortToast("GetMessagesListing size OK: size=" + size);
+ }
+
+ @Override
+ public void onGetMessagesListingSizeError() {
+ goToState(Job.IDLE);
+ new MonkeyEvent("map-getmessageslisting-size", false).send();
+ shortToast("GetMessagesListing size FAILED");
+ }
+
+ @Override
+ public void onEventReport(BluetoothMapEventReport eventReport) {
+ String msgType = (eventReport.getMsgType() != null) ?
+ eventReport.getMsgType().toString() : getString(R.string.blank);
+
+ new MonkeyEvent("map-eventreport", true)
+ .addReplyParam("type", eventReport.getType().toString())
+ .addReplyParam("handle", eventReport.getHandle())
+ .addReplyParam("folder", eventReport.getFolder())
+ .addReplyParam("old_folder", eventReport.getOldFolder())
+ .addReplyParam("msg_type", msgType)
+ .send();
+ }
+ });
+
+ ProfileService.MapSessionData map = mProfileService.getMapSessionData(mMasInstanceId);
+ if (map != null) {
+ if (map.getFolderListing != null) {
+ clearFolderList();
+ mAdapterFolders.addAll(map.getFolderListing);
+ }
+
+ if (map.getMessagesListing != null) {
+ mAdapterMessages.clear();
+ mAdapterMessages.addAll(map.getMessagesListing);
+ updateListEmptyView(false);
+ }
+
+ if (map.getMessage != null) {
+ updateMessage(map.getMessage);
+ }
+ }
+
+ mTextViewCurrentFolder.setText(mProfileService.getMapClient(mMasInstanceId)
+ .getCurrentPath());
+
+ updateUi(true);
+
+ if (mStartingGetMessageHandle != null) {
+ getMessage(mStartingGetMessageHandle, CharsetType.UTF_8, false);
+ mStartingGetMessageHandle = null;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mProfileService = null;
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+
+ ActivityHelper.initialize(this, R.layout.activity_map_test);
+ ActivityHelper.setActionBarTitle(this, R.string.title_map_test);
+
+ mMasInstanceId = intent.getIntExtra(ProfileService.EXTRA_MAP_INSTANCE_ID, -1);
+
+ if (ProfileService.ACTION_MAP_GET_MESSAGE.equals(intent.getAction())) {
+ mStartingGetMessageHandle = intent
+ .getStringExtra(ProfileService.EXTRA_MAP_MESSAGE_HANDLE);
+ }
+
+ if (mMasInstanceId < 0) {
+ Log.e(TAG, "Cannot start MAP activity without instance information");
+ finish();
+ }
+
+ intent = new Intent(MapTestActivity.this, ProfileService.class);
+ bindService(intent, mMapServiceConnection, BIND_AUTO_CREATE);
+
+ mViewFlipper = (ViewFlipper) findViewById(R.id.maptest_viewflipper);
+ mTextViewCurrentFolder = (TextView) findViewById(R.id.maptest_nav_current);
+ mSpinnerFolders = (Spinner) findViewById(R.id.maptest_nav_folders);
+ mAdapterFolders = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
+ mSpinnerFolders.setAdapter(mAdapterFolders);
+ mEditTextMaxListCountFolders = (EditText) findViewById(R.id.maptest_nav_list_max);
+ mEditTextMaxListCountFolders.setText(String.valueOf(MAX_LIST_COUNT_DEFAULT));
+ mEditTextListStartOffsetFolders = (EditText) findViewById(R.id.maptest_nav_list_offset);
+ mEditTextListStartOffsetFolders.setText(String.valueOf(LIST_START_OFFSET_DEFAULT));
+ mEditTextMaxListCountMessages = (EditText) findViewById(R.id.maptest_msglist_max);
+ mEditTextMaxListCountMessages.setText(String.valueOf(MAX_LIST_COUNT_DEFAULT));
+ mEditTextListStartOffsetMessages = (EditText) findViewById(R.id.maptest_msglist_offset);
+ mEditTextListStartOffsetMessages.setText(String.valueOf(LIST_START_OFFSET_DEFAULT));
+ mListViewMessages = (ListView) findViewById(R.id.maptest_msglist_lv);
+ mListViewMessages.setEmptyView(findViewById(R.id.maptest_msglist_empty));
+ mModelMessages = new ArrayList<BluetoothMapMessage>();
+ mAdapterMessages = new BluetoothMapMessageAdapter();
+ mListViewMessages.setAdapter(mAdapterMessages);
+ mListViewMessages.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ String handle = mModelMessages.get(position).getHandle();
+
+ getMessage(handle, CharsetType.UTF_8, false);
+ }
+ });
+ registerForContextMenu(mListViewMessages);
+
+ mEditTextSubjectLength = (EditText) findViewById(R.id.maptest_msglist_subject_len);
+ mEditTextSubjectLength.setText(String.valueOf(SUBJECT_LENGTH_DEFAULT));
+ mViewBmsgHandle = (EditText) findViewById(R.id.map_msg_handle);
+ mViewBmsgStatus = (EditText) findViewById(R.id.maptest_bmsg_status);
+ mViewBmsgType = (EditText) findViewById(R.id.maptest_bmsg_type);
+ mViewBmsgFolder = (EditText) findViewById(R.id.maptest_bmsg_folder);
+ mViewBmsgEncoding = (EditText) findViewById(R.id.maptest_bbody_encoding);
+ mViewBmsgCharset = (EditText) findViewById(R.id.maptest_bbody_charset);
+ mViewBmsgLanguage = (EditText) findViewById(R.id.maptest_bbody_language);
+ mViewBmsgContents = (EditText) findViewById(R.id.maptest_message);
+ mViewBmsgOrig = (EditText) findViewById(R.id.maptest_orig);
+ mViewBmsgRcpt = (EditText) findViewById(R.id.maptest_rcpt);
+
+ clearFolderList();
+ updateListEmptyView(false);
+
+ mActionBar = getActionBar();
+ mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+ ActionBar.TabListener tabListener = new ActionBar.TabListener() {
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ mCurrentTab = tab.getText().toString();
+
+ if (mCurrentTab.equals(TAB_LIST)) {
+ mViewFlipper.setDisplayedChild(0);
+ } else if (mCurrentTab.equals(TAB_PREVIEW)) {
+ mViewFlipper.setDisplayedChild(1);
+ } else if (mCurrentTab.equals(TAB_EDIT)) {
+ mViewFlipper.setDisplayedChild(2);
+ }
+
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ }
+ };
+
+ for (String tab : mActionBarTabsNames) {
+ mActionBar.addTab(
+ mActionBar.newTab()
+ .setText(tab)
+ .setTabListener(tabListener));
+
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode == REQUEST_CODE_GET_PARAMETERS) {
+ if (resultCode == RESULT_OK) {
+ mMessageListingParameters = (int) data.getLongExtra("result", 0);
+ }
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Logger.v(TAG, "onStart()");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Logger.v(TAG, "onStop()");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Logger.v(TAG, "onDestroy()");
+ if (mProfileService != null) {
+ mProfileService.setMapCallback(mMasInstanceId, null);
+ }
+ unbindService(mMapServiceConnection);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ mActionBarMenu = menu;
+
+ getMenuInflater().inflate(R.menu.menu_map_test, menu);
+
+ boolean connected = false;
+
+ if (mProfileService != null) {
+ connected = (mProfileService.getMapClient(mMasInstanceId).getState() == BluetoothMasClient.ConnectionState.CONNECTED);
+ }
+
+ menu.findItem(R.id.menu_map_connect).setVisible(!connected);
+ menu.findItem(R.id.menu_map_disconnect).setVisible(connected);
+ menu.findItem(R.id.menu_map_update_inbox).setVisible(connected);
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_map_connect:
+ onClickConnect();
+ break;
+ case R.id.menu_map_disconnect:
+ onClickDisconnect();
+ break;
+ case R.id.menu_map_update_inbox:
+ onClickUpdateInbox();
+ break;
+ case R.id.menu_map_goto_inbox:
+ goToFolder("telecom/msg/inbox");
+ break;
+ case R.id.menu_map_goto_outbox:
+ goToFolder("telecom/msg/outbox");
+ break;
+ case R.id.menu_map_goto_draft:
+ goToFolder("telecom/msg/draft");
+ break;
+ default:
+ Logger.w(TAG, "Unknown item selected.");
+ break;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void clearFolderList() {
+ mAdapterFolders.clear();
+ mAdapterFolders.add(".");
+ }
+
+ private void onClickConnect() {
+ mProfileService.getMapClient(mMasInstanceId).connect();
+ goToState(Job.CONNECT);
+ }
+
+ private void onClickDisconnect() {
+ mProfileService.getMapClient(mMasInstanceId).disconnect();
+ goToState(Job.DISCONNECT);
+ }
+
+ private void onClickUpdateInbox() {
+ mProfileService.getMapClient(mMasInstanceId).updateInbox();
+ goToState(Job.UPDATE_INBOX);
+ }
+
+ private VCardEntry createVcard(BluetoothMapBmessage.Type type, String val) {
+ VCardEntry vcard = new VCardEntry();
+ VCardProperty prop = new VCardProperty();
+
+ prop.setName(VCardConstants.PROPERTY_N);
+ prop.setValues(val);
+ vcard.addProperty(prop);
+
+ if (BluetoothMapBmessage.Type.EMAIL.equals(type)) {
+ prop.setName(VCardConstants.PROPERTY_EMAIL);
+ } else {
+ prop.setName(VCardConstants.PROPERTY_TEL);
+ }
+ vcard.addProperty(prop);
+
+ return vcard;
+ }
+
+ public void onClickPushMessage(View v) {
+ boolean transparent = ((CheckBox) findViewById(R.id.map_msg_push_transparent)).isChecked();
+ boolean retry = ((CheckBox) findViewById(R.id.map_msg_push_retry)).isChecked();
+ Spinner typeView = (Spinner) findViewById(R.id.bmsgedit_type);
+ Spinner encView = (Spinner) findViewById(R.id.bmsgedit_encoding);
+ EditText contentsView = (EditText) findViewById(R.id.bmsgedit_contents);
+ int charset = ((RadioGroup) findViewById(R.id.map_msg_push_charset))
+ .getCheckedRadioButtonId();
+
+ BluetoothMapBmessage bmsg = new BluetoothMapBmessage();
+
+ bmsg.setStatus(BluetoothMapBmessage.Status.UNREAD);
+
+ for (BluetoothMapBmessage.Type t : BluetoothMapBmessage.Type.values()) {
+ if (t.name().equals(typeView.getSelectedItem().toString())) {
+ bmsg.setType(t);
+ break;
+ }
+ }
+
+ if (charset != R.id.map_msg_push_charset_native) {
+ bmsg.setCharset("UTF-8");
+ }
+
+ if (encView.getSelectedItemPosition() > 0) {
+ bmsg.setEncoding(encView.getSelectedItem().toString());
+ }
+
+ bmsg.setFolder(mProfileService.getMapClient(mMasInstanceId).getCurrentPath());
+
+ for (String rcpt : mEditOriginators) {
+ bmsg.addRecipient(createVcard(bmsg.getType(), rcpt));
+ }
+
+ for (String orig : mEditRecipients) {
+ bmsg.addRecipient(createVcard(bmsg.getType(), orig));
+ }
+
+ bmsg.setBodyContent(contentsView.getText().toString());
+
+ mProfileService
+ .getMapClient(mMasInstanceId)
+ .pushMessage(
+ null,
+ bmsg,
+ charset == R.id.map_msg_push_charset_native ? BluetoothMasClient.CharsetType.NATIVE
+ : BluetoothMasClient.CharsetType.UTF_8, transparent, retry);
+
+ goToState(Job.PUSH_MESSAGE);
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ int instanceId = intent.getIntExtra(ProfileService.EXTRA_MAP_INSTANCE_ID, -1);
+
+ if (instanceId < 0) {
+ // don't care if instance info is missing
+ return;
+ }
+
+ // in case request is for different MAS instance than activity is
+ // running, we'll just restart activity using the same intent to get
+ // proper MAS instance
+ if (instanceId != mMasInstanceId) {
+ finish();
+ overridePendingTransition(0, 0);
+ startActivity(intent);
+ overridePendingTransition(0, 0);
+ return;
+ }
+
+ if (ProfileService.ACTION_MAP_GET_MESSAGE.equals(intent.getAction())) {
+ String handle = intent.getStringExtra(ProfileService.EXTRA_MAP_MESSAGE_HANDLE);
+ getMessage(handle, CharsetType.UTF_8, false);
+ }
+ }
+
+ public void onClickDeleteMessage(View view) {
+ if (mPreviewMsgHandle != null) {
+ mProfileService.getMapClient(mMasInstanceId).setMessageDeletedStatus(mPreviewMsgHandle,
+ true);
+ goToState(Job.DELETE_MESSAGE);
+ }
+ }
+
+ public void onClickSetStatus(View view) {
+ if (mMapBmessage == null || mPreviewMsgHandle == null) {
+ return;
+ }
+
+ if (mMapBmessage.getStatus().equals(BluetoothMapBmessage.Status.READ)) {
+ mProfileService.getMapClient(mMasInstanceId).setMessageReadStatus(mPreviewMsgHandle,
+ false);
+ goToState(Job.SET_STATUS_UNREAD);
+ } else {
+ mProfileService.getMapClient(mMasInstanceId).setMessageReadStatus(mPreviewMsgHandle,
+ true);
+ goToState(Job.SET_STATUS_READ);
+ }
+ }
+
+ public void onClickSetPathRoot(View view) {
+ mProfileService.getMapClient(mMasInstanceId).setFolderRoot();
+ goToState(Job.SET_PATH);
+ }
+
+ public void onClickSetPathUp(View view) {
+ mProfileService.getMapClient(mMasInstanceId).setFolderUp();
+ goToState(Job.SET_PATH);
+ }
+
+ public void onClickSetPathEnter(View view) {
+ String folder = mSpinnerFolders.getSelectedItem().toString();
+
+ // do not let go to current folder
+ if (folder.equals(".")) {
+ return;
+ }
+
+ mProfileService.getMapClient(mMasInstanceId).setFolderDown(folder);
+ goToState(Job.SET_PATH);
+ }
+
+ public void onClickGetFolderListing(View view) {
+ int count = Integer.parseInt(mEditTextMaxListCountFolders.getText().toString());
+ int offset = Integer.parseInt(mEditTextListStartOffsetFolders.getText().toString());
+
+ try {
+ mProfileService.getMapClient(mMasInstanceId).getFolderListing(count, offset);
+ goToState(Job.GET_FOLDER_LISTING);
+ } catch (IllegalArgumentException e) {
+ Toast.makeText(this,
+ "GetFolderListing FAILED: illegal arguments (" + e.getMessage() + ")",
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ public void onClickGetFolderListingSize(View view) {
+ mProfileService.getMapClient(mMasInstanceId).getFolderListingSize();
+ goToState(Job.GET_FOLDER_LISTING_SIZE);
+ }
+
+ public void onClickMessageParameters(View view) {
+ Intent intent = new Intent(MapTestActivity.this, MessageFilterActivity.class);
+ intent.putExtra("filter", (long) mMessageListingParameters);
+ startActivityForResult(intent, REQUEST_CODE_GET_PARAMETERS);
+ }
+
+ public MessagesFilter getLocalMessageFilter() {
+ MessagesFilter messageFilter = new MessagesFilter();
+ messageFilter.setMessageType(mMessageType);
+ messageFilter.setOriginator(mOriginator);
+ messageFilter.setPeriod(mPeriodBegin, mPeriodEnd);
+ messageFilter.setPriority(mPriority);
+ messageFilter.setReadStatus(mReadStatus);
+ messageFilter.setRecipient(mRecipient);
+ return messageFilter;
+ }
+
+ public class MessageFilterDialogFragment extends DialogFragment {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ View dialogView = MapTestActivity.this.getLayoutInflater().inflate(
+ R.layout.messages_filter, null);
+
+ final MessageFilterHolder holder = new MessageFilterHolder(dialogView);
+ holder.populate();
+
+ final AlertDialog.Builder alertBuilder = new AlertDialog.Builder(MapTestActivity.this);
+ alertBuilder.setView(dialogView);
+ alertBuilder
+ .setTitle(getResources().getString(R.string.dialog_title_create_msg_filter));
+ alertBuilder.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ holder.save();
+ }
+ });
+ alertBuilder.setNegativeButton(android.R.string.cancel, null);
+ alertBuilder.setNeutralButton(getResources().getString(R.string.clear), null);
+
+ // After click on button with assigned listener in function
+ // setNeutralButton, the dialog automatically will close. To avoid
+ // it,
+ // onClickListener for this button have to be assigned manually.
+ final AlertDialog alertDialog = alertBuilder.create();
+ alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
+ @Override
+ public void onShow(DialogInterface dialog) {
+ Button clearButton = alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL);
+ clearButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ holder.clear();
+ }
+ });
+ }
+ });
+
+ return alertDialog;
+ }
+ }
+
+ public void onClickGetMessagesFilter(View view) {
+ new MessageFilterDialogFragment().show(getFragmentManager(), "msg_filter");
+ }
+
+ class MessageFilterHolder {
+ CheckBox type_sms_gsm = null;
+ CheckBox type_sms_cdma = null;
+ CheckBox type_email = null;
+ CheckBox type_mms = null;
+ RadioButton status_read_all = null;
+ RadioButton status_unread = null;
+ RadioButton status_read = null;
+ RadioButton priority_all = null;
+ RadioButton priority_high = null;
+ RadioButton priority_non_high = null;
+ EditText period_begin = null;
+ EditText period_end = null;
+ EditText recipient = null;
+ EditText originator = null;
+ ImageButton pickPeriodBegin = null;
+ ImageButton pickPeriodEnd = null;
+
+ MessageFilterHolder(View row) {
+ type_sms_gsm = (CheckBox) row.findViewById(R.id.map_filter_type_sms_gsm);
+ type_sms_cdma = (CheckBox) row.findViewById(R.id.map_filter_type_sms_cdma);
+ type_email = (CheckBox) row.findViewById(R.id.map_filter_type_email);
+ type_mms = (CheckBox) row.findViewById(R.id.map_filter_type_mms);
+ status_read_all = (RadioButton) row.findViewById(R.id.map_filter_read_status_all);
+ status_unread = (RadioButton) row.findViewById(R.id.map_filter_read_status_unread);
+ status_read = (RadioButton) row.findViewById(R.id.map_filter_read_status_read);
+ priority_all = (RadioButton) row.findViewById(R.id.map_filter_priority_all);
+ priority_high = (RadioButton) row.findViewById(R.id.map_filter_priority_high);
+ priority_non_high = (RadioButton) row.findViewById(R.id.map_filter_status_non_high);
+ period_begin = (EditText) row.findViewById(R.id.map_filter_period_begin);
+ period_end = (EditText) row.findViewById(R.id.map_filter_period_end);
+ recipient = (EditText) row.findViewById(R.id.map_filter_recipient);
+ originator = (EditText) row.findViewById(R.id.map_filter_originator);
+ pickPeriodBegin = (ImageButton) row.findViewById(R.id.map_pick_data_period_begin);
+ pickPeriodBegin.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ new DateTimePicker(period_begin).pick();
+ }
+ });
+ pickPeriodEnd = (ImageButton) row.findViewById(R.id.map_pick_data_period_end);
+ pickPeriodEnd.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ new DateTimePicker(period_end).pick();
+ }
+ });
+ }
+
+ void populate() {
+ type_sms_gsm.setChecked((mMessageType & MessagesFilter.MESSAGE_TYPE_SMS_GSM) != 0);
+ type_sms_cdma.setChecked((mMessageType & MessagesFilter.MESSAGE_TYPE_SMS_CDMA) != 0);
+ type_email.setChecked((mMessageType & MessagesFilter.MESSAGE_TYPE_EMAIL) != 0);
+ type_mms.setChecked((mMessageType & MessagesFilter.MESSAGE_TYPE_MMS) != 0);
+ status_read_all.setChecked(mReadStatus == MessagesFilter.READ_STATUS_ANY);
+ status_read.setChecked(mReadStatus == MessagesFilter.READ_STATUS_READ);
+ status_unread.setChecked(mReadStatus == MessagesFilter.READ_STATUS_UNREAD);
+ priority_all.setChecked(mPriority == MessagesFilter.PRIORITY_ANY);
+ priority_high.setChecked(mPriority == MessagesFilter.PRIORITY_HIGH);
+ priority_non_high.setChecked(mPriority == MessagesFilter.PRIORITY_NON_HIGH);
+
+ if (mPeriodBegin != null) {
+ period_begin.setText(mSimpleDateFormat.format(mPeriodBegin));
+ } else {
+ period_begin.setText(null);
+ }
+
+ if (mPeriodEnd != null) {
+ period_end.setText(mSimpleDateFormat.format(mPeriodEnd));
+ } else {
+ period_end.setText(null);
+ }
+
+ recipient.setText(mRecipient);
+ originator.setText(mOriginator);
+ }
+
+ void clear() {
+ mMessageType = MessagesFilter.MESSAGE_TYPE_ALL;
+ mPeriodBegin = null;
+ mPeriodEnd = null;
+ mReadStatus = MessagesFilter.READ_STATUS_ANY;
+ mRecipient = null;
+ mOriginator = null;
+ mPriority = MessagesFilter.PRIORITY_ANY;
+
+ populate();
+ }
+
+ void save() {
+ mMessageType = MessagesFilter.MESSAGE_TYPE_ALL;
+
+ if (type_sms_gsm.isChecked()) {
+ mMessageType |= MessagesFilter.MESSAGE_TYPE_SMS_GSM;
+ }
+
+ if (type_sms_cdma.isChecked()) {
+ mMessageType |= MessagesFilter.MESSAGE_TYPE_SMS_CDMA;
+ }
+
+ if (type_email.isChecked()) {
+ mMessageType |= MessagesFilter.MESSAGE_TYPE_EMAIL;
+ }
+
+ if (type_mms.isChecked()) {
+ mMessageType |= MessagesFilter.MESSAGE_TYPE_MMS;
+ }
+
+ if (status_read.isChecked()) {
+ mReadStatus = MessagesFilter.READ_STATUS_READ;
+ } else if (status_unread.isChecked()) {
+ mReadStatus = MessagesFilter.READ_STATUS_UNREAD;
+ } else {
+ mReadStatus = MessagesFilter.READ_STATUS_ANY;
+ }
+
+ if (priority_high.isChecked()) {
+ mPriority = MessagesFilter.PRIORITY_HIGH;
+ } else if (priority_non_high.isChecked()) {
+ mPriority = MessagesFilter.PRIORITY_NON_HIGH;
+ } else {
+ mPriority = MessagesFilter.PRIORITY_ANY;
+ }
+
+ try {
+ mPeriodBegin = mSimpleDateFormat.parse(period_begin.getText().toString());
+ } catch (ParseException e) {
+ mPeriodBegin = null;
+ Logger.e(TAG, "Exception during parse begin period!");
+ }
+
+ try {
+ mPeriodEnd = mSimpleDateFormat.parse(period_end.getText().toString());
+ } catch (ParseException e) {
+ mPeriodEnd = null;
+ Logger.e(TAG, "Exception during parse end period!");
+ }
+
+ mRecipient = recipient.getText().toString();
+ mOriginator = originator.getText().toString();
+ }
+ }
+
+ public void onClickGetMessagesListing(View view) {
+ String folder = mSpinnerFolders.getSelectedItem().toString();
+ int maxListCount = Integer.parseInt(
+ mEditTextMaxListCountMessages.getText().toString());
+ int listStartOffset = Integer.parseInt(
+ mEditTextListStartOffsetMessages.getText().toString());
+ int subjectLength = Integer.parseInt(
+ mEditTextSubjectLength.getText().toString());
+
+ if (folder.equals(".")) {
+ folder = "";
+ }
+
+ try {
+ mProfileService.getMapClient(mMasInstanceId).getMessagesListing(folder,
+ mMessageListingParameters,
+ getLocalMessageFilter(), subjectLength, maxListCount, listStartOffset);
+
+ goToState(Job.GET_MESSAGE_LISTING);
+ updateListEmptyView(true);
+ } catch (IllegalArgumentException e) {
+ Toast.makeText(this,
+ "GetMessagesListing FAILED: illegal arguments (" + e.getMessage() + ")",
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ public void updateListEmptyView(boolean working) {
+ View progressBar = findViewById(R.id.maptest_msglist_progressbar);
+ View textView = findViewById(R.id.maptest_msglist_empty);
+
+ if (working) {
+ textView.setVisibility(View.GONE);
+ mListViewMessages.setEmptyView(progressBar);
+ } else {
+ progressBar.setVisibility(View.GONE);
+ mListViewMessages.setEmptyView(textView);
+ }
+ }
+
+ public void onClickGetMessageListingSize(View view) {
+ mProfileService.getMapClient(mMasInstanceId).getMessagesListingSize();
+ goToState(Job.GET_MESSAGE_LISTING_SIZE);
+ }
+
+ class BluetoothMapMessageAdapter extends ArrayAdapter<BluetoothMapMessage> {
+ BluetoothMapMessageAdapter() {
+ super(MapTestActivity.this, android.R.layout.simple_list_item_1, mModelMessages);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = convertView;
+
+ if (v == null) {
+ v = getLayoutInflater().inflate(R.layout.message_row, parent, false);
+ }
+
+ BluetoothMapMessage msg = mModelMessages.get(position);
+
+ ((TextView) v.findViewById(R.id.message_row_type)).setText(msg.getType().toString());
+ ((TextView) v.findViewById(R.id.message_row_handle)).setText(msg.getHandle());
+
+ ((TextView) v.findViewById(R.id.message_row_flag_text))
+ .setTextColor(msg.isText() ? Color.YELLOW : Color.DKGRAY);
+ ((TextView) v.findViewById(R.id.message_row_flag_read))
+ .setTextColor(msg.isRead() ? Color.YELLOW : Color.DKGRAY);
+ ((TextView) v.findViewById(R.id.message_row_flag_sent))
+ .setTextColor(msg.isSent() ? Color.YELLOW : Color.DKGRAY);
+ ((TextView) v.findViewById(R.id.message_row_flag_drm))
+ .setTextColor(msg.isProtected() ? Color.YELLOW : Color.DKGRAY);
+ ((TextView) v.findViewById(R.id.message_row_flag_prio))
+ .setTextColor(msg.isPriority() ? Color.YELLOW : Color.DKGRAY);
+
+ ((TextView) v.findViewById(R.id.message_row_subject)).setText(msg.getSubject());
+
+ ((TextView) v.findViewById(R.id.message_row_date))
+ .setText(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(msg.getDateTime()));
+
+ ((TextView) v.findViewById(R.id.message_row_from)).setText(msg.getSenderAddressing());
+ if (msg.getSenderAddressing() != null && !msg.getSenderAddressing().isEmpty()) {
+ ((TextView) v.findViewById(R.id.message_row_from_lbl))
+ .setVisibility(View.VISIBLE);
+ } else {
+ ((TextView) v.findViewById(R.id.message_row_from_lbl))
+ .setVisibility(View.INVISIBLE);
+ }
+
+ ((TextView) v.findViewById(R.id.message_row_to)).setText(msg.getRecipientAddressing());
+ if (msg.getRecipientAddressing() != null && !msg.getRecipientAddressing().isEmpty()) {
+ ((TextView) v.findViewById(R.id.message_row_to_lbl)).setVisibility(View.VISIBLE);
+ } else {
+ ((TextView) v.findViewById(R.id.message_row_to_lbl)).setVisibility(View.INVISIBLE);
+ }
+
+ return v;
+ }
+ }
+
+ class DateTimePicker {
+ View view = null;
+ EditText editText = null;
+ DatePicker dataPicker = null;
+ TimePicker timePicker = null;
+
+ DateTimePicker(EditText ref) {
+ editText = ref;
+
+ view = getLayoutInflater().inflate(R.layout.date_time_picker, null);
+
+ dataPicker = (DatePicker) view.findViewById(R.id.date_picker);
+ timePicker = (TimePicker) view.findViewById(R.id.time_picker);
+ timePicker.setIs24HourView(true);
+
+ String oldValue = editText.getText().toString();
+
+ if (oldValue != null && !oldValue.isEmpty() && !oldValue.equals("")) {
+ try {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(mSimpleDateFormat.parse(oldValue));
+ dataPicker.updateDate(cal.get(Calendar.YEAR),
+ cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
+ timePicker.setCurrentHour(cal.get(Calendar.HOUR_OF_DAY));
+ timePicker.setCurrentMinute(cal.get(Calendar.MINUTE));
+ } catch (ParseException e) {
+ Logger.e(TAG, "Parse exception in DataTimePicker!");
+ }
+ }
+ }
+
+ public void pick() {
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(MapTestActivity.this);
+ alertBuilder.setView(view);
+ alertBuilder.setTitle(getResources().getString(R.string.dialog_title_pick_date_time));
+ alertBuilder.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int year = dataPicker.getYear();
+ int month = dataPicker.getMonth();
+ int day = dataPicker.getDayOfMonth();
+ int hour = timePicker.getCurrentHour();
+ int minute = timePicker.getCurrentMinute();
+
+ editText.setText(mSimpleDateFormat.format(new GregorianCalendar(
+ year, month, day, hour, minute, 0).getTime()));
+ }
+ });
+ alertBuilder.setNegativeButton(android.R.string.cancel, null);
+ alertBuilder.show();
+ }
+ }
+
+ private void resetPreviewEditUi() {
+ mViewBmsgHandle.setText("");
+
+ mViewBmsgStatus.setText("");
+ mViewBmsgType.setText("");
+ mViewBmsgFolder.setText("");
+
+ mViewBmsgEncoding.setText("");
+ mViewBmsgCharset.setText("");
+ mViewBmsgLanguage.setText("");
+
+ mViewBmsgContents.setText("");
+
+ mViewBmsgOrig.setText("");
+ mViewBmsgRcpt.setText("");
+
+ mPreviewMsgHandle = null;
+ mMapBmessage = null;
+
+ invalidateOptionsMenu();
+ }
+
+ private void updateUi(boolean invalidateOptionsMenu) {
+ if (mListTouchables == null) {
+ LinearLayout lay = (LinearLayout) findViewById(R.id.maptest_tab_list);
+ mListTouchables = lay.getTouchables();
+ }
+
+ for (View view : mListTouchables) {
+ view.setEnabled(mProfileService.getMapClient(mMasInstanceId).getState() == BluetoothMasClient.ConnectionState.CONNECTED
+ && mCurrentJob == Job.IDLE);
+ }
+
+ if (invalidateOptionsMenu) {
+ invalidateOptionsMenu();
+ }
+ }
+
+ private void goToState(Job job) {
+ Logger.v(TAG, "Switch to " + job + " state from " + mCurrentJob.toString() + ".");
+ mCurrentJob = job;
+ updateUi(true);
+ }
+
+ private void updateMessage(BluetoothMapBmessage message) {
+ StringBuilder sb;
+
+ mPreviewMsgHandle = mPendingGetMessageHandle;
+ mPendingGetMessageHandle = null;
+
+ mMapBmessage = message;
+
+ boolean isRead = mMapBmessage.getStatus().equals(BluetoothMapBmessage.Status.READ);
+ ((Button) (findViewById(R.id.map_msg_set_status))).setText(
+ isRead ? getString(R.string.map_set_unread) : getString(R.string.map_set_read));
+
+ mActionBar.selectTab(mActionBar.getTabAt(1));
+
+ mViewBmsgHandle.setText(mPreviewMsgHandle);
+
+ mViewBmsgStatus.setText(message.getStatus().toString());
+ mViewBmsgType.setText(message.getType().toString());
+ mViewBmsgFolder.setText(message.getFolder());
+
+ mViewBmsgEncoding.setText(message.getEncoding());
+ mViewBmsgCharset.setText(message.getCharset());
+ mViewBmsgLanguage.setText(message.getLanguage());
+
+ mViewBmsgContents.setText(message.getBodyContent());
+
+ sb = new StringBuilder();
+ for (VCardEntry vcard : message.getOriginators()) {
+ sb.append(vcard.getDisplayName());
+ if (message.getType().equals(BluetoothMapBmessage.Type.EMAIL)) {
+ if (vcard.getEmailList() != null && vcard.getEmailList().size() > 0) {
+ sb.append(" <").append(vcard.getEmailList().get(0).getAddress()).append(">");
+ }
+ } else {
+ if (vcard.getPhoneList() != null && vcard.getPhoneList().size() > 0) {
+ sb.append(" <").append(vcard.getPhoneList().get(0).getNumber()).append(">");
+ }
+ }
+ sb.append(", ");
+ }
+ mViewBmsgOrig.setText(sb.toString());
+
+ sb = new StringBuilder();
+ for (VCardEntry vcard : message.getRecipients()) {
+ sb.append(vcard.getDisplayName());
+ if (message.getType().equals(BluetoothMapBmessage.Type.EMAIL)) {
+ if (vcard.getEmailList() != null && vcard.getEmailList().size() > 0) {
+ sb.append(" <").append(vcard.getEmailList().get(0).getAddress()).append(">");
+ }
+ } else {
+ if (vcard.getPhoneList() != null && vcard.getPhoneList().size() > 0) {
+ sb.append(" <").append(vcard.getPhoneList().get(0).getNumber()).append(">");
+ }
+ }
+ sb.append(", ");
+ }
+ mViewBmsgRcpt.setText(sb.toString());
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+
+ menu.add(0, 1, 0, "status -> READ");
+ menu.add(0, 2, 0, "status -> UNREAD");
+ menu.add(0, 3, 0, "status -> DELETED");
+ menu.add(0, 4, 0, "status -> UNDELETED");
+ menu.add(0, 5, 0, "get in native charset");
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+
+ BluetoothMapMessage bmsg = mAdapterMessages.getItem(info.position);
+
+ switch (item.getItemId()) {
+ case 1:
+ case 2:
+ mProfileService.getMapClient(mMasInstanceId).setMessageReadStatus(bmsg.getHandle(),
+ item.getItemId() == 1);
+ goToState(Job.SET_STATUS_READ);
+ break;
+ case 3:
+ case 4:
+ mProfileService.getMapClient(mMasInstanceId).setMessageDeletedStatus(
+ bmsg.getHandle(), item.getItemId() == 3);
+ goToState(Job.DELETE_MESSAGE);
+ break;
+ case 5:
+ getMessage(bmsg.getHandle(), CharsetType.NATIVE, false);
+ break;
+ }
+
+ return true;
+ }
+
+ public void onClickRemovePeople(View v) {
+ if (v.getId() == R.id.bmsgedit_orig_del) {
+ new StringListDialogFragment(mEditOriginators).show(getFragmentManager(), "orig_del");
+ } else if (v.getId() == R.id.bmsgedit_rcpt_del) {
+ new StringListDialogFragment(mEditRecipients).show(getFragmentManager(), "rcpt_del");
+ }
+ }
+
+ public void onClickAddPerson(View v) {
+ if (v.getId() == R.id.bmsgedit_rcpt_add) {
+ new GetTextDialogFragment().show(getFragmentManager(), "rcpt_add");
+ } else if (v.getId() == R.id.bmsgedit_orig_add) {
+ new GetTextDialogFragment().show(getFragmentManager(), "orig_add");
+ }
+ }
+
+ @Override
+ public void onGetTextDialogPositive(DialogFragment dialog, String text) {
+ if ("rcpt_add".equals(dialog.getTag())) {
+ mEditRecipients.add(text);
+ } else if ("orig_add".equals(dialog.getTag())) {
+ mEditOriginators.add(text);
+ }
+
+ refreshEditPeople();
+ }
+
+ @Override
+ public void onStringListDialogPositive(DialogFragment dialog, ArrayList<String> elements) {
+ if ("rcpt_del".equals(dialog.getTag())) {
+ mEditRecipients.clear();
+ mEditRecipients.addAll(elements);
+ } else if ("orig_del".equals(dialog.getTag())) {
+ mEditOriginators.clear();
+ mEditOriginators.addAll(elements);
+ }
+
+ refreshEditPeople();
+ }
+
+ private void refreshEditPeople() {
+ StringBuilder sb;
+
+ sb = new StringBuilder();
+ for (String s : mEditOriginators) {
+ sb.append(s).append(", ");
+ }
+ ((TextView) findViewById(R.id.bmsgedit_orig)).setText(sb.toString());
+
+ sb = new StringBuilder();
+ for (String s : mEditRecipients) {
+ sb.append(s).append(", ");
+ }
+ ((TextView) findViewById(R.id.bmsgedit_rcpt)).setText(sb.toString());
+ }
+
+ public void onClickGetMessage(View v) {
+ String handle = mViewBmsgHandle.getText().toString();
+ int selCharset = ((RadioGroup) findViewById(R.id.map_msg_get_charset))
+ .getCheckedRadioButtonId();
+ boolean attachment = ((CheckBox) findViewById(R.id.map_msg_get_attachment)).isChecked();
+
+ CharsetType charset = (selCharset == R.id.map_msg_get_charset_native ? CharsetType.NATIVE
+ : CharsetType.UTF_8);
+
+ if (!getMessage(handle, charset, attachment)) {
+ Toast.makeText(MapTestActivity.this, "GetMessage rejected, check parameters",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void onClickCopyMessage(View v) {
+ String type = mViewBmsgType.getText().toString();
+ Spinner typeView = (Spinner) findViewById(R.id.bmsgedit_type);
+
+ for (int i = 0; i < typeView.getCount(); i++) {
+ if (type.equals(typeView.getItemAtPosition(i))) {
+ typeView.setSelection(i);
+ break;
+ }
+ }
+
+ String enc = mViewBmsgEncoding.getText().toString();
+ Spinner encView = (Spinner) findViewById(R.id.bmsgedit_encoding);
+
+ for (int i = 0; i < encView.getCount(); i++) {
+ if (enc.equals(encView.getItemAtPosition(i))) {
+ encView.setSelection(i);
+ break;
+ }
+ }
+
+ ((EditText) findViewById(R.id.bmsgedit_contents)).setText(mViewBmsgContents.getText());
+ }
+
+ private void goToFolder(String dst) {
+ mSetPathQueue = new ArrayDeque<String>(Arrays.asList(dst.split("/")));
+
+ mProfileService.getMapClient(mMasInstanceId).setFolderRoot();
+ }
+
+ private boolean getMessage(String handle, CharsetType charset, boolean attachments) {
+ if (mProfileService == null || handle == null) {
+ return false;
+ }
+
+ BluetoothMasClient cli = mProfileService.getMapClient(mMasInstanceId);
+
+ if (!cli.getMessage(handle, charset, attachments)) {
+ return false;
+ }
+
+ goToState(Job.GET_MESSAGE);
+
+ mPendingGetMessageHandle = handle;
+
+ return true;
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MessageFilterActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MessageFilterActivity.java
new file mode 100644
index 0000000..2bfb977
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MessageFilterActivity.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.os.Bundle;
+
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+
+public class MessageFilterActivity extends FilterActivity {
+
+ private final String TAG = "MessageFilterActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Logger.v(TAG, "onCreate()");
+ ActivityHelper.initialize(this, R.layout.activity_message_filter);
+ ActivityHelper.setActionBarTitle(this, R.string.title_message_filter);
+ }
+
+ @Override
+ protected void fillParameters() {
+ addParameter(0, R.id.param_subject);
+ addParameter(1, R.id.param_datatime);
+ addParameter(2, R.id.param_sender_name);
+ addParameter(3, R.id.param_sender_addressing);
+ addParameter(4, R.id.param_receipent_name);
+ addParameter(5, R.id.param_receipent_addressing);
+ addParameter(6, R.id.param_type);
+ addParameter(7, R.id.param_size);
+ addParameter(8, R.id.param_reception_status);
+ addParameter(9, R.id.param_text);
+ addParameter(10, R.id.param_attachment_size);
+ addParameter(11, R.id.param_priority);
+ addParameter(12, R.id.param_read);
+ addParameter(13, R.id.param_sent);
+ addParameter(14, R.id.param_protected);
+ addParameter(15, R.id.param_replayto_addressing);
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MonkeyActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MonkeyActivity.java
new file mode 100644
index 0000000..e8839c1
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MonkeyActivity.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class MonkeyActivity extends Activity {
+
+ private static final String TAG = "MonkeyActivity";
+
+ public static final String ACTION_MONKEY = "com.qualcomm.bluetooth.action.MONKEY";
+
+ public static final String ACTION_MONKEY_QUERY = "com.qualcomm.bluetooth.action.MONKEY_QUERY";
+
+ public static final String EXTRA_OP = "extra.op";
+ public static final String EXTRA_DIALOG_TAG = "extra.dialogTag";
+ public static final String EXTRA_ID = "extra.id";
+ public static final String EXTRA_TEXT = "extra.text";
+
+ public static final String OP_DUMP = "dump";
+ public static final String OP_SELECT_TAB = "selectTab";
+ public static final String OP_CLICK = "click";
+ public static final String OP_LONG_CLICK = "longClick";
+ public static final String OP_SET_TEXT = "setText";
+ public static final String OP_SELECT_BY_TEXT = "selectByText";
+ public static final String OP_CLICK_BUTTON = "clickButton";
+ public static final String OP_CLICK_ACTION_BAR_MENU_BY_TEXT = "clickActionBarMenuByText";
+
+ protected Menu mActionBarMenu = null;
+
+ BroadcastReceiver mReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.d(TAG, "received " + action);
+
+ if (ACTION_MONKEY.equals(action)) {
+ String op = intent.getStringExtra(EXTRA_OP);
+ String dialogTag = intent.getStringExtra(EXTRA_DIALOG_TAG);
+ String id = intent.getStringExtra(EXTRA_ID);
+ String text = intent.getStringExtra(EXTRA_TEXT);
+
+ if (OP_DUMP.equals(op)) {
+ getViewById(dialogTag, null);
+ } else if (OP_SELECT_TAB.equals(op)) {
+ monkeySelectTab(text);
+ } else if (OP_CLICK.equals(op)) {
+ monkeyClick(dialogTag, id, false);
+ } else if (OP_LONG_CLICK.equals(op)) {
+ monkeyClick(dialogTag, id, true);
+ } else if (OP_SET_TEXT.equals(op)) {
+ monkeySetText(dialogTag, id, text);
+ } else if (OP_SELECT_BY_TEXT.equals(op)) {
+ monkeySelectByText(dialogTag, id, text);
+ } else if (OP_CLICK_BUTTON.equals(op)) {
+ monkeyClickButton(dialogTag, id);
+ } else if (OP_CLICK_ACTION_BAR_MENU_BY_TEXT.equals(op)) {
+ monkeyClickActionBarMenuByText(text);
+ }
+ } else if (ACTION_MONKEY_QUERY.equals(action)) {
+ String op = intent.getStringExtra(EXTRA_OP);
+
+ if (op != null) {
+ onMonkeyQuery(op, intent.getExtras());
+ }
+ }
+ }
+ };
+
+ @Override
+ protected void onResume() {
+ Log.v(TAG, "onResume");
+ super.onResume();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_MONKEY);
+ filter.addAction(ACTION_MONKEY_QUERY);
+ registerReceiver(mReceiver, filter);
+ }
+
+ @Override
+ protected void onPause() {
+ Log.v(TAG, "onPause");
+ super.onPause();
+
+ unregisterReceiver(mReceiver);
+ }
+
+ private View getViewById(View view, String id, String tag, int nest) {
+ if (view == null) {
+ return null;
+ }
+
+ String tid = null;
+
+ final int vid = view.getId();
+ final Resources res = view.getResources();
+ if (vid != View.NO_ID && vid != 0 && res != null) {
+ if ((vid & 0xff000000) == 0x7f000000) {
+ tid = res.getResourceEntryName(vid);
+
+ if (id != null) {
+ boolean match = false;
+ if (id.equals(tid)) {
+ if (tag == null) {
+ match = true;
+ } else {
+ Object ttag = view.getTag();
+ match = (ttag != null && ttag.toString().equals(tag));
+ }
+ }
+
+ if (match) {
+ return view;
+ }
+ }
+ }
+ }
+
+ if (id == null) {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < nest; i++) {
+ sb.append(" ");
+ }
+
+ sb.append(view.getClass().getSimpleName());
+
+ if (view instanceof TextView) {
+ String txt = ((TextView) view).getText().toString();
+
+ if (txt.length() > 0) {
+ sb.append(" \"").append(txt).append("\"");
+ }
+ }
+
+ if (tid != null) {
+ sb.append(" [").append(tid);
+ if (view.getTag() != null) {
+ sb.append("#").append(view.getTag().toString());
+ }
+ sb.append("]");
+ }
+
+ Log.v("DUMP", sb.toString());
+ }
+
+ View ret = null;
+
+ if (view instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) view;
+
+ int count = vg.getChildCount();
+ for (int idx = 0; idx < count && ret == null; idx++) {
+ ret = getViewById(vg.getChildAt(idx), id, tag, nest + 1);
+ }
+ }
+
+ return ret;
+ }
+
+ private View getViewById(String dialogTag, String id) {
+ View root;
+ String tag = null;
+
+ if (dialogTag != null) {
+ Fragment frag = getFragmentManager().findFragmentByTag(dialogTag);
+
+ if (!(frag instanceof DialogFragment)) {
+ return null;
+ }
+
+ root = ((DialogFragment) frag).getDialog().findViewById(android.R.id.content);
+ } else {
+ root = findViewById(android.R.id.content);
+ }
+
+ if (id != null) {
+ int hashPos = id.lastIndexOf("#");
+ if (hashPos > 0) {
+ tag = id.substring(hashPos + 1);
+ id = id.substring(0, hashPos);
+ }
+ }
+
+ return getViewById(root, id, tag, 0);
+ }
+
+ private void monkeySelectTab(String text) {
+ ActionBar ab = getActionBar();
+
+ if (ab == null) {
+ Log.w(TAG, "selectTab not found");
+ return;
+ }
+
+ int count = ab.getTabCount();
+ for (int idx = 0; idx < count; idx++) {
+ ActionBar.Tab tab = ab.getTabAt(idx);
+ if (tab.getText().toString().toLowerCase().equals(text.toLowerCase())) {
+ ab.selectTab(tab);
+ return;
+ }
+ }
+ }
+
+ private void monkeyClick(String dialogTag, String id, boolean longClick) {
+ View view = getViewById(dialogTag, id);
+
+ if (view == null) {
+ Log.w(TAG, "click(" + dialogTag + ", " + id + ") not found");
+ return;
+ }
+
+ if (longClick) {
+ view.performLongClick();
+ } else {
+ view.performClick();
+ }
+ }
+
+ private void monkeySetText(String dialogTag, String id, String text) {
+ View view = getViewById(dialogTag, id);
+
+ if (view == null || !(view instanceof TextView)) {
+ Log.w(TAG, "setText(" + dialogTag + ", " + id + ") not found");
+ return;
+ }
+
+ TextView txt = (TextView) view;
+ txt.setText(text);
+ }
+
+ private void monkeySelectByText(String dialogTag, String id, String text) {
+ View view = getViewById(dialogTag, id);
+
+ if (view == null || !(view instanceof AdapterView)) {
+ Log.w(TAG, "selectByText(" + dialogTag + ", " + id + ") not found");
+ return;
+ }
+
+ @SuppressWarnings("unchecked")
+ AdapterView<Adapter> adv = (AdapterView<Adapter>) view;
+
+ int count = adv.getCount();
+ for (int idx = 0; idx < count; idx++) {
+ if (adv.getItemAtPosition(idx).toString().equals(text)) {
+ adv.setSelection(idx);
+ return;
+ }
+ }
+ }
+
+ private void monkeyClickButton(String dialogTag, String id) {
+ if (dialogTag == null) {
+ return;
+ }
+
+ int which;
+
+ try {
+ which = Integer.parseInt(id);
+ } catch (NumberFormatException e) {
+ return;
+ }
+
+ Fragment frag = getFragmentManager().findFragmentByTag(dialogTag);
+
+ if (!(frag instanceof DialogFragment)) {
+ return;
+ }
+
+ Dialog dlg = ((DialogFragment) frag).getDialog();
+
+ if (!(dlg instanceof AlertDialog)) {
+ return;
+ }
+
+ Button btn = ((AlertDialog) dlg).getButton(which);
+
+ btn.performClick();
+ }
+
+ private void monkeyClickActionBarMenuByText(String text) {
+ if (mActionBarMenu == null || text == null) {
+ return;
+ }
+
+ for (int idx = 0; idx < mActionBarMenu.size(); idx++) {
+ MenuItem item = mActionBarMenu.getItem(idx);
+
+ String title = item.getTitle().toString();
+
+ if (title.toLowerCase().equals(text.toLowerCase())) {
+ mActionBarMenu.performIdentifierAction(item.getItemId(), 0);
+ break;
+ }
+ }
+ }
+
+ protected void onMonkeyQuery(String op, Bundle params) {
+ // do nothing
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/PbapTestActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/PbapTestActivity.java
new file mode 100644
index 0000000..4815411
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/PbapTestActivity.java
@@ -0,0 +1,978 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
+import android.app.FragmentTransaction;
+import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.RadioButton;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.ViewFlipper;
+
+import com.android.vcard.VCardEntry;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapCard;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapClient;
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.services.IPbapServiceCallback;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+import org.codeaurora.bluetooth.bttestapp.util.MonkeyEvent;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class PbapTestActivity extends MonkeyActivity implements IBluetoothConnectionObserver {
+
+ private final String TAG = "PbapTestActivity";
+
+ /*
+ * Class constants.
+ */
+ private static final int REQUEST_CODE_GET_FILTER_FOR_DOWNLOAD_TAB = 0;
+ private static final int REQUEST_CODE_GET_FILTER_FOR_VCARD_TAB = 1;
+
+ private ArrayDeque<String> mSetPathQueue = null;
+
+ private static final short MAX_COUNT_DEFAULT_VALUE = 0;
+ private static final short OFFSET_DEFAULT_VALUE = 0;
+ private static final String HANDLE_DEFAULT_VALUE = "0.vcf";
+
+ /*
+ * Common UI.
+ */
+ private ActionBar mActionBar = null;
+ private ViewFlipper mViewFlipper = null;
+ /*
+ * Download functionality UI.
+ */
+ private Spinner mDownloadSpinner = null;
+ private RadioButton mRadioButtonVCard21 = null;
+ private RadioButton mRadioButtonVCard30 = null;
+ private EditText mEditTextDownloadMaxListCount = null;
+ private EditText mEditTextDownloadOffsetValue = null;
+ private Button mButtonFilter = null;
+ private Button mButtonDownloadSearch = null;
+
+ private ProgressBar mDownloadProgressBar = null;
+ private TextView mTextViewDownloadNothingFound = null;
+ private ListView mListViewDownloadContacts = null;
+
+ private RadioButton mRadioButtonSearchName = null;
+ private RadioButton mRadioButtonSearchNumber = null;
+ private RadioButton mRadioButtonSearchSound = null;
+ private EditText mEditTextSearchValue = null;
+ private EditText mEditTextBrowseMaxListCount = null;
+ private EditText mEditTextBrowseOffsetValue = null;
+ private RadioButton mRadioButtonOrderUnordered = null;
+ private RadioButton mRadioButtonOrderAlphabetical = null;
+ private RadioButton mRadioButtonOrderIndexed = null;
+ private RadioButton mRadioButtonOrderPhonetic = null;
+ private Button mButtonBrowseSearch = null;
+
+ private ProgressBar mBrowseProgressBar = null;
+ private TextView mTextViewBrowseNothingFound = null;
+ private ListView mListViewBrowseContacts = null;
+
+ /*
+ * vCard Details functionality.
+ */
+ private Spinner mVcardSpinner = null;
+ private VcardView mVcardView = null;
+ private EditText mEditTextHandleValue = null;
+
+ /*
+ * Download local variables.
+ */
+ private long mDownloadValueFilter = 0;
+ private byte mDownloadValueCardType = BluetoothPbapClient.VCARD_TYPE_21;
+ private int mDownloadValueMaxCount = MAX_COUNT_DEFAULT_VALUE;
+ private int mDownloadValueOffset = OFFSET_DEFAULT_VALUE;
+
+ /*
+ * Browse local variables.
+ */
+ private byte mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_DEFAULT;
+ private byte mBrowseValueSearchAttr = BluetoothPbapClient.SEARCH_ATTR_NAME;
+ private int mBrowseValueMaxCount = MAX_COUNT_DEFAULT_VALUE;
+ private int mBrowseValueOffset = OFFSET_DEFAULT_VALUE;
+
+ /*
+ * Action bar tabs.
+ */
+ private ActionBar.Tab mDownloadTab = null;
+ private ActionBar.Tab mBrowseTab = null;
+ private ActionBar.Tab mVcardTab = null;
+
+ /*
+ * vCard local variables.
+ */
+ private String mVcardHandleValue = HANDLE_DEFAULT_VALUE;
+ private long mVcardValueFilter = 0;
+ private byte mVcardValueCardType = BluetoothPbapClient.VCARD_TYPE_21;
+
+ /*
+ * Download adapter.
+ */
+ private VCardEntryAdapter mVCardEntryAdapter = null;
+
+ OnItemClickListener mOnDownloadItemClickListener = new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ VCardEntry vcard = (VCardEntry) parent.getAdapter().getItem(position);
+ mVcardView.setVCardEntry(vcard);
+ mActionBar.selectTab(mVcardTab);
+ }
+ };
+
+ /*
+ * Browse adapter.
+ */
+ private BluetoothPbapCardAdapter mBluetoothPbapCardAdapter = null;
+
+ OnItemClickListener mOnBrowseItemClickListener = new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ BluetoothPbapCard pbacpCard = (BluetoothPbapCard) parent.getAdapter().getItem(position);
+ mProfileService.getPbapClient().pullVcardEntry(pbacpCard.handle);
+ }
+ };
+
+ /*
+ * PBAP Service.
+ */
+ private ProfileService mProfileService = null;
+ private final IPbapServiceCallback mPbapServiceCallback = new IPbapServiceCallback() {
+
+ @Override
+ public void onSetPhoneBookDone() {
+ if (mSetPathQueue != null && mSetPathQueue.size() > 0) {
+ String next = mSetPathQueue.removeFirst();
+
+ setPhonebookFolder(next);
+ } else {
+ new MonkeyEvent("pbap-setphonebook", true).send();
+ }
+ }
+
+ @Override
+ public void onPullPhoneBookDone(ArrayList<VCardEntry> list, int missedCalls) {
+ mVCardEntryAdapter.clear();
+ mVCardEntryAdapter.addAll(list);
+
+ stopProgressBarDownload();
+
+ /* send event for monkeyrunner */
+ MonkeyEvent evt = new MonkeyEvent("pbap-pullphonebook", true);
+ evt.addReplyParam("size", list.size());
+ for (VCardEntry card : list) {
+ evt.addExtReply(BluetoothPbapCard.jsonifyVcardEntry(card));
+ }
+ evt.send();
+
+ Toast.makeText(PbapTestActivity.this, "Missed calls=" + missedCalls, Toast.LENGTH_SHORT)
+ .show();
+ }
+
+ @Override
+ public void onPullVcardListingDone(ArrayList<BluetoothPbapCard> list, int missedCalls) {
+ mBluetoothPbapCardAdapter.clear();
+ mBluetoothPbapCardAdapter.addAll(list);
+ stopProgressBarBrowse();
+
+ /* send event for monkeyrunner */
+ new MonkeyEvent("pbap-pullvcardlisting", true)
+ .addReplyParam("size", list.size())
+ .addExtReply(list)
+ .send();
+
+ Toast.makeText(PbapTestActivity.this, "Missed calls=" + missedCalls, Toast.LENGTH_SHORT)
+ .show();
+ }
+
+ @Override
+ public void onPullVcardEntryDone(VCardEntry vcard) {
+ Logger.v(TAG, "onReceivedPbapPullVcardEntryDone()");
+
+ /* send event for monkeyrunner */
+ new MonkeyEvent("pbap-pullvcardentry", true)
+ .addExtReply(BluetoothPbapCard.jsonifyVcardEntry(vcard))
+ .send();
+
+ mVcardView.setVCardEntry(vcard);
+ mActionBar.selectTab(mVcardTab);
+ }
+
+ @Override
+ public void onPullPhoneBookSizeDone(int size, int type) {
+ /* send event for monkeyrunner */
+ if (type == 0) {
+ new MonkeyEvent("pbap-pullphonebook-size", true)
+ .addReplyParam("size", size)
+ .send();
+ } else {
+ new MonkeyEvent("pbap-pullvcardlisting-size", true)
+ .addReplyParam("size", size)
+ .send();
+ }
+
+ Toast.makeText(PbapTestActivity.this, "size=" + size, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onSetPhoneBookError() {
+ Logger.e(TAG, "Received from PBAP set phone book error.");
+
+ mSetPathQueue = null;
+
+ new MonkeyEvent("pbap-setphonebook", false).send();
+ }
+
+ @Override
+ public void onPullPhoneBookError() {
+ Logger.e(TAG, "Received from PBAP pull phone book error.");
+ stopProgressBarDownload();
+ Toast.makeText(PbapTestActivity.this, "PullPhoneBook FAILED", Toast.LENGTH_LONG).show();
+ new MonkeyEvent("pbap-pullphonebook", false).send();
+ }
+
+ @Override
+ public void onPullVcardListingError() {
+ Logger.e(TAG, "Received from PBAP listing error.");
+ stopProgressBarBrowse();
+ Toast.makeText(PbapTestActivity.this, "PullvCardListing FAILED", Toast.LENGTH_LONG)
+ .show();
+ new MonkeyEvent("pbap-pullvcardlisting", false).send();
+ }
+
+ @Override
+ public void onPullVcardEntryError() {
+ Logger.e(TAG, "Received from PBAP vCard entry error.");
+ Toast.makeText(PbapTestActivity.this, "PullvCardEntry FAILED", Toast.LENGTH_LONG)
+ .show();
+ new MonkeyEvent("pbap-pullvcardentry", false).send();
+ }
+
+ @Override
+ public void onPullPhoneBookSizeError() {
+ Logger.e(TAG, "Received from PBAP pull phone book size error.");
+ Toast.makeText(PbapTestActivity.this, "PullPhoneBook size FAILED", Toast.LENGTH_LONG)
+ .show();
+ new MonkeyEvent("pbap-pullphonebook-size", false).send();
+ }
+
+ @Override
+ public void onPullVcardListingSizeError() {
+ Logger.e(TAG, "Received from PBAP pull vCard listing sizeerror.");
+ Toast.makeText(PbapTestActivity.this, "PullvCardListing size FAILED", Toast.LENGTH_LONG)
+ .show();
+ new MonkeyEvent("pbap-pullvcardlisting-size", false).send();
+ }
+
+ @Override
+ public void onSessionConnected() {
+ Toast.makeText(PbapTestActivity.this, "PBAP session connected", Toast.LENGTH_SHORT)
+ .show();
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onSessionDisconnected() {
+ Toast.makeText(PbapTestActivity.this, "PBAP session disconnected", Toast.LENGTH_SHORT)
+ .show();
+ invalidateOptionsMenu();
+ }
+ };
+
+ /*
+ * PBAP Service Connection.
+ */
+ private final ServiceConnection mPbapServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Logger.v(TAG, "onServiceConnected()");
+ mProfileService = ((ProfileService.LocalBinder) service).getService();
+ mProfileService.setPbapCallback(mPbapServiceCallback);
+
+ ProfileService.PbapSessionData pbap = mProfileService.getPbapSessionData();
+
+ if (pbap.pullPhoneBook != null) {
+ mVCardEntryAdapter.clear();
+ mVCardEntryAdapter.addAll(pbap.pullPhoneBook);
+ stopProgressBarDownload();
+ }
+
+ if (pbap.pullVcardListing != null) {
+ mBluetoothPbapCardAdapter.clear();
+ mBluetoothPbapCardAdapter.addAll(pbap.pullVcardListing);
+ stopProgressBarBrowse();
+ }
+
+ if (pbap.pullVcardEntry != null) {
+ mVcardView.setVCardEntry(pbap.pullVcardEntry);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Logger.v(TAG, "onServiceDisconnected()");
+ mProfileService = null;
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Logger.v(TAG, "onCreate()");
+ ActivityHelper.initialize(this, R.layout.activity_pbap_test);
+ ActivityHelper.setActionBarTitle(this, R.string.title_pbap_test);
+
+ prepareActionBar();
+ prepareUserInterfaceDownload();
+ prepareUserInterfaceBrowse();
+ prepareUserInterfaceVcard();
+
+ mBluetoothPbapCardAdapter = new BluetoothPbapCardAdapter();
+ mListViewBrowseContacts.setAdapter(mBluetoothPbapCardAdapter);
+
+ mVCardEntryAdapter = new VCardEntryAdapter();
+ mListViewDownloadContacts.setAdapter(mVCardEntryAdapter);
+
+ BluetoothConnectionReceiver.registerObserver(this);
+
+ // bind to PBAP service
+ Intent intent = new Intent(this, ProfileService.class);
+ bindService(intent, mPbapServiceConnection, BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Logger.v(TAG, "onStart()");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Logger.v(TAG, "onStop()");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Logger.v(TAG, "onDestroy()");
+
+ mProfileService.setPbapCallback(null);
+
+ unbindService(mPbapServiceConnection);
+ BluetoothConnectionReceiver.removeObserver(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Logger.v(TAG, "onResume()");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Logger.v(TAG, "onPause()");
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ mActionBarMenu = menu;
+
+ getMenuInflater().inflate(R.menu.menu_pbap_test, menu);
+
+ if (mProfileService.getPbapClient().getState() != BluetoothPbapClient.ConnectionState.DISCONNECTED) {
+ menu.findItem(R.id.menu_pbap_disconnect).setVisible(true);
+ } else {
+ menu.findItem(R.id.menu_pbap_connect).setVisible(true);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_pbap_connect:
+ mProfileService.getPbapClient().connect();
+ break;
+ case R.id.menu_pbap_disconnect:
+ mProfileService.getPbapClient().disconnect();
+ break;
+ default:
+ Logger.w(TAG, "Unknown item selected.");
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ Logger.v(TAG, "onActivityResult()");
+
+ if (resultCode != RESULT_OK) {
+ Logger.w(TAG, "Result code is not ok for req: " + requestCode + ".");
+ return;
+ }
+
+ if (requestCode == REQUEST_CODE_GET_FILTER_FOR_DOWNLOAD_TAB) {
+ long result = data.getLongExtra("result", -1);
+
+ if (result != -1) {
+ Logger.d(TAG, "Received download tab filter " + result + ".");
+ mDownloadValueFilter = result;
+ } else {
+ Logger.w(TAG, "Somethings go wrong here!");
+ }
+ }
+
+ if (requestCode == REQUEST_CODE_GET_FILTER_FOR_VCARD_TAB) {
+ long result = data.getLongExtra("result", -1);
+
+ if (result != -1) {
+ Logger.d(TAG, "Received vcard tab filter " + result + ".");
+ mVcardValueFilter = result;
+ } else {
+ Logger.w(TAG, "Somethings go wrong here!");
+ }
+ }
+ }
+
+ private void prepareUserInterfaceDownload() {
+ mDownloadSpinner = (Spinner) findViewById(R.id.pbap_download_spinner);
+ mButtonFilter = (Button) findViewById(R.id.pbap_download_filter_button);
+ mButtonFilter.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mButtonFilter.onClick()");
+
+ Intent intent = new Intent(PbapTestActivity.this, VcardFilterActivity.class);
+ intent.putExtra("filter", mDownloadValueFilter);
+ intent.putExtra("type", mDownloadValueCardType);
+ startActivityForResult(intent, REQUEST_CODE_GET_FILTER_FOR_DOWNLOAD_TAB);
+ }
+ });
+
+ mEditTextDownloadMaxListCount = (EditText) findViewById(R.id.pbap_download_max_list_count);
+ mEditTextDownloadMaxListCount.setText(String.valueOf(mDownloadValueMaxCount));
+
+ mEditTextDownloadOffsetValue = (EditText) findViewById(R.id.pbap_download_offset_value);
+ mEditTextDownloadOffsetValue.setText(String.valueOf(mDownloadValueOffset));
+
+ mRadioButtonVCard21 = (RadioButton) findViewById(R.id.pbap_download_vcard21);
+ mRadioButtonVCard21.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButonVCard21.onClick()");
+ mDownloadValueCardType = BluetoothPbapClient.VCARD_TYPE_21;
+ }
+ });
+
+ mRadioButtonVCard30 = (RadioButton) findViewById(R.id.pbap_download_vcard30);
+ mRadioButtonVCard30.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButonVCard30.onClick()");
+ mDownloadValueCardType = BluetoothPbapClient.VCARD_TYPE_30;
+ }
+ });
+
+ mButtonDownloadSearch = (Button) findViewById(R.id.pbap_download_search);
+ mButtonDownloadSearch.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mButtonDownloadSearch.onClick()");
+ runPbapTestDownload();
+ }
+ });
+
+ mDownloadProgressBar = (ProgressBar) findViewById(R.id.pbap_download_progress_bar);
+ mTextViewDownloadNothingFound = (TextView) findViewById(R.id.pbap_download_test_list_empty);
+ mListViewDownloadContacts = (ListView) findViewById(R.id.pbap_download_listview_contacts_list);
+ mListViewDownloadContacts.setOnItemClickListener(mOnDownloadItemClickListener);
+
+ mDownloadProgressBar.setVisibility(View.GONE);
+ mListViewDownloadContacts.setVisibility(View.GONE);
+ }
+
+ private void prepareUserInterfaceBrowse() {
+ mRadioButtonSearchName = (RadioButton) findViewById(R.id.pbap_browse_name);
+ mRadioButtonSearchName.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButtonSearchName.onClick()");
+ mBrowseValueSearchAttr = BluetoothPbapClient.SEARCH_ATTR_NAME;
+ }
+ });
+
+ mRadioButtonSearchNumber = (RadioButton) findViewById(R.id.pbap_browse_number);
+ mRadioButtonSearchNumber.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButtonSearchNumber.onClick()");
+ mBrowseValueSearchAttr = BluetoothPbapClient.SEARCH_ATTR_NUMBER;
+ }
+ });
+
+ mRadioButtonSearchSound = (RadioButton) findViewById(R.id.pbap_browse_sound);
+ mRadioButtonSearchSound.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButtonSearchSound.onClick()");
+ mBrowseValueSearchAttr = BluetoothPbapClient.SEARCH_ATTR_SOUND;
+ }
+ });
+
+ mEditTextSearchValue = (EditText) findViewById(R.id.pbap_browse_search_value);
+
+ mEditTextBrowseMaxListCount = (EditText) findViewById(R.id.pbap_browse_max_list_count);
+ mEditTextBrowseMaxListCount.setText(String.valueOf(mBrowseValueMaxCount));
+
+ mEditTextBrowseOffsetValue = (EditText) findViewById(R.id.pbap_browse_offset_value);
+ mEditTextBrowseOffsetValue.setText(String.valueOf(mBrowseValueOffset));
+
+ mRadioButtonOrderUnordered = (RadioButton) findViewById(R.id.pbap_browse_unordered);
+ mRadioButtonOrderUnordered.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButtonOrderUnordered.onClick()");
+ mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_DEFAULT;
+ }
+ });
+
+ mRadioButtonOrderAlphabetical = (RadioButton) findViewById(R.id.pbap_browse_alphabetical);
+ mRadioButtonOrderAlphabetical.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButtonOrderAlphabetical.onClick()");
+ mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_ALPHABETICAL;
+ }
+ });
+
+ mRadioButtonOrderIndexed = (RadioButton) findViewById(R.id.pbap_browse_indexed);
+ mRadioButtonOrderIndexed.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButtonOrderIndexed.onClick()");
+ mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_INDEXED;
+ }
+ });
+
+ mRadioButtonOrderPhonetic = (RadioButton) findViewById(R.id.pbap_browse_phonetic);
+ mRadioButtonOrderPhonetic.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mRadioButtonOrderPhonetic.onClick()");
+ mBrowseValueOrder = BluetoothPbapClient.ORDER_BY_PHONETIC;
+ }
+ });
+
+ mButtonBrowseSearch = (Button) findViewById(R.id.pbap_browse_search);
+ mButtonBrowseSearch.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.v(TAG, "mButtonBrowseSearch.onClick()");
+ runPbapTestBrowse();
+ }
+ });
+
+ mBrowseProgressBar = (ProgressBar) findViewById(R.id.pbap_browse_progress_bar);
+ mTextViewBrowseNothingFound = (TextView) findViewById(R.id.pbap_browse_test_list_empty);
+ mListViewBrowseContacts = (ListView) findViewById(R.id.pbap_browse_listview_contacts_list);
+ mListViewBrowseContacts.setOnItemClickListener(mOnBrowseItemClickListener);
+
+ mBrowseProgressBar.setVisibility(View.GONE);
+ mListViewBrowseContacts.setVisibility(View.GONE);
+ }
+
+ private void prepareUserInterfaceVcard() {
+ mVcardSpinner = (Spinner) findViewById(R.id.pbap_vcard_spinner);
+ mVcardView = (VcardView) findViewById(R.id.pbap_vcard_vcardview);
+
+ mEditTextHandleValue = (EditText) findViewById(R.id.pbap_vcard_header);
+ mEditTextHandleValue.setText(mVcardHandleValue);
+ }
+
+ private void prepareActionBar() {
+ mActionBar = getActionBar();
+
+ if (mActionBar != null) {
+ mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ mDownloadTab = mActionBar.newTab().setText(R.string.download);
+ mBrowseTab = mActionBar.newTab().setText(R.string.browse);
+ mVcardTab = mActionBar.newTab().setText(R.string.vcard);
+
+ mViewFlipper = (ViewFlipper) findViewById(R.id.pbap_test_viewflipper);
+
+ mDownloadTab.setTabListener(new TabListener() {
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ /* NoP */
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ mViewFlipper.setDisplayedChild(0);
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ /* NoP */
+ }
+ });
+
+ mBrowseTab.setTabListener(new TabListener() {
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ /* NoP */
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ mViewFlipper.setDisplayedChild(1);
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ /* NoP */
+ }
+ });
+
+ mVcardTab.setTabListener(new TabListener() {
+
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ /* NoP */
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ mViewFlipper.setDisplayedChild(2);
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ /* NoP */
+ }
+ });
+
+ mActionBar.addTab(mDownloadTab);
+ mActionBar.addTab(mBrowseTab);
+ mActionBar.addTab(mVcardTab);
+ }
+ }
+
+ private void runPbapTestDownload() {
+ Logger.v(TAG, "runPbapTestDownload()");
+
+ try {
+ mDownloadValueMaxCount = Integer.parseInt(mEditTextDownloadMaxListCount.getText()
+ .toString());
+ } catch (NumberFormatException e) {
+ mDownloadValueMaxCount = MAX_COUNT_DEFAULT_VALUE;
+ }
+
+ try {
+ mDownloadValueOffset = Integer.parseInt(mEditTextDownloadOffsetValue.getText()
+ .toString());
+ } catch (NumberFormatException e) {
+ mDownloadValueOffset = OFFSET_DEFAULT_VALUE;
+ }
+
+ // Refresh UI EditTexts value if some are blank after edit.
+ mEditTextDownloadMaxListCount.setText(String.valueOf(mDownloadValueMaxCount));
+ mEditTextDownloadOffsetValue.setText(String.valueOf(mDownloadValueOffset));
+
+ try {
+ if (mProfileService.getPbapClient().pullPhoneBook(
+ mDownloadSpinner.getSelectedItem().toString(), mDownloadValueFilter,
+ mDownloadValueCardType, mDownloadValueMaxCount, mDownloadValueOffset)) {
+ startProgressBarDownload();
+ } else {
+ Toast.makeText(this, "PullPhoneBook FAILED", Toast.LENGTH_LONG).show();
+ }
+ } catch (IllegalArgumentException e) {
+ Toast.makeText(this,
+ "PullPhoneBook FAILED: illegal arguments (" + e.getMessage() + ")",
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void startProgressBarDownload() {
+ mDownloadProgressBar.setVisibility(View.VISIBLE);
+ mListViewDownloadContacts.setVisibility(View.GONE);
+ mTextViewDownloadNothingFound.setVisibility(View.GONE);
+ }
+
+ private void stopProgressBarDownload() {
+ mDownloadProgressBar.setVisibility(View.GONE);
+ if (mVCardEntryAdapter.getCount() == 0) {
+ mTextViewDownloadNothingFound.setVisibility(View.VISIBLE);
+ mListViewDownloadContacts.setVisibility(View.GONE);
+ } else {
+ mTextViewDownloadNothingFound.setVisibility(View.GONE);
+ mListViewDownloadContacts.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void runPbapTestBrowse() {
+ Logger.v(TAG, "runPbapTestBrowse()");
+
+ byte order = mBrowseValueOrder;
+ String searchValue = mEditTextSearchValue.getText().toString();
+
+ try {
+ mBrowseValueMaxCount = Integer.parseInt(mEditTextBrowseMaxListCount.getText()
+ .toString());
+ } catch (NumberFormatException e) {
+ mBrowseValueMaxCount = MAX_COUNT_DEFAULT_VALUE;
+ }
+
+ try {
+ mBrowseValueOffset = Integer.parseInt(mEditTextBrowseOffsetValue.getText().toString());
+ } catch (NumberFormatException e) {
+ mBrowseValueOffset = OFFSET_DEFAULT_VALUE;
+ }
+
+ // Refresh UI EditTexts value if some are blank after edit.
+ mEditTextBrowseMaxListCount.setText(String.valueOf(mBrowseValueMaxCount));
+ mEditTextBrowseOffsetValue.setText(String.valueOf(mBrowseValueOffset));
+
+ try {
+ boolean started;
+
+ if (searchValue != "" && !searchValue.isEmpty()) {
+ started = mProfileService.getPbapClient().pullVcardListing(null, order,
+ mBrowseValueSearchAttr, searchValue, mBrowseValueMaxCount,
+ mBrowseValueOffset);
+ } else {
+ started = mProfileService.getPbapClient().pullVcardListing(null, order,
+ mBrowseValueMaxCount, mBrowseValueOffset);
+ }
+
+ if (started) {
+ startProgressBarBrowse();
+ } else {
+ Toast.makeText(this, "PullvCardListing FAILED", Toast.LENGTH_LONG).show();
+ }
+ } catch (IllegalArgumentException e) {
+ Toast.makeText(this,
+ "PullvCardListing FAILED: illegal arguments (" + e.getMessage() + ")",
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void startProgressBarBrowse() {
+ mBrowseProgressBar.setVisibility(View.VISIBLE);
+ mListViewBrowseContacts.setVisibility(View.GONE);
+ mTextViewBrowseNothingFound.setVisibility(View.GONE);
+ }
+
+ private void stopProgressBarBrowse() {
+ mBrowseProgressBar.setVisibility(View.GONE);
+ if (mBluetoothPbapCardAdapter.getCount() == 0) {
+ mTextViewBrowseNothingFound.setVisibility(View.VISIBLE);
+ mListViewBrowseContacts.setVisibility(View.GONE);
+ } else {
+ mTextViewBrowseNothingFound.setVisibility(View.GONE);
+ mListViewBrowseContacts.setVisibility(View.VISIBLE);
+ }
+ }
+
+ class VCardEntryAdapter extends ArrayAdapter<VCardEntry> {
+ VCardEntryAdapter() {
+ super(PbapTestActivity.this, android.R.layout.simple_list_item_1);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = convertView;
+ boolean defaultPhoto = true;
+
+ if (row == null) {
+ LayoutInflater inflater = getLayoutInflater();
+ row = inflater.inflate(R.layout.vcard_row, null);
+ }
+
+ VCardEntry tmp = getItem(position);
+
+ ((TextView) row.findViewById(R.id.vcard_title)).setText(tmp.getDisplayName());
+
+ if (tmp.getPhotoList() != null && tmp.getPhotoList().size() > 0) {
+ byte[] data = tmp.getPhotoList().get(0).getBytes();
+ try {
+ if (data != null) {
+ Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
+ ((ImageView) row.findViewById(R.id.vcard_photo)).setImageBitmap(bmp);
+ defaultPhoto = false;
+ }
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ if (defaultPhoto) {
+ ((ImageView) row.findViewById(R.id.vcard_photo))
+ .setImageResource(R.drawable.ic_contact_picture);
+ }
+
+ return row;
+ }
+ }
+
+ class BluetoothPbapCardAdapter extends ArrayAdapter<BluetoothPbapCard> {
+ BluetoothPbapCardAdapter() {
+ super(PbapTestActivity.this, android.R.layout.simple_list_item_1);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = convertView;
+
+ if (row == null) {
+ LayoutInflater inflater = getLayoutInflater();
+ row = inflater.inflate(R.layout.vcard_row, null);
+ }
+
+ BluetoothPbapCard tmp = getItem(position);
+
+ StringBuilder sb = new StringBuilder();
+
+ if (tmp.firstName != null) {
+ sb.append(tmp.firstName).append(" ");
+ }
+ sb.append(tmp.lastName);
+
+ ((TextView) row.findViewById(R.id.vcard_title)).setText(sb.toString());
+ return row;
+ }
+ }
+
+ @Override
+ public void onDeviceChanged(BluetoothDevice device) {
+ Logger.v(TAG, "onDeviceChanged()");
+ /* NoP */
+ }
+
+ @Override
+ public void onDeviceDisconected() {
+ Logger.e(TAG, "BT device disconnected!");
+ }
+
+ public void onClick_download_getsize(View v) {
+ mProfileService.getPbapClient().pullPhoneBookSize(
+ mDownloadSpinner.getSelectedItem().toString());
+ }
+
+ public void onClick_browse_getsize(View v) {
+ mProfileService.getPbapClient().pullVcardListingSize("");
+ }
+
+ public void onClickVcardFilterAttributes(View v) {
+ Intent intent = new Intent(PbapTestActivity.this, VcardFilterActivity.class);
+ intent.putExtra("filter", mVcardValueFilter);
+ intent.putExtra("type", mVcardValueCardType);
+ startActivityForResult(intent, REQUEST_CODE_GET_FILTER_FOR_VCARD_TAB);
+ }
+
+ public void onClickRadioButtonVcardType21(View v) {
+ mVcardValueCardType = BluetoothPbapClient.VCARD_TYPE_21;
+ }
+
+ public void onClickRadioButtonVcardType30(View v) {
+ mVcardValueCardType = BluetoothPbapClient.VCARD_TYPE_30;
+ }
+
+ public void onClick_setPhonebook(View v) {
+ int id = getResources().getIdentifier(v.getTag().toString(), "id", getPackageName());
+
+ Spinner spinner = (Spinner) findViewById(id);
+
+ if (spinner != null) {
+ setPhonebook(spinner.getSelectedItem().toString());
+ }
+ }
+
+ public void onClickVcardButtonGet(View v) {
+ mVcardHandleValue = mEditTextHandleValue.getText().toString();
+
+ if (mVcardHandleValue.isEmpty() || mVcardHandleValue == null) {
+ mVcardHandleValue = HANDLE_DEFAULT_VALUE;
+ mEditTextHandleValue.setText(mVcardHandleValue);
+ }
+
+ mProfileService.getPbapClient().pullVcardEntry(mVcardHandleValue, mVcardValueFilter,
+ mVcardValueCardType);
+ }
+
+ private void setPhonebook(String dst) {
+ dst = dst.replaceFirst("\\.vcf$", "");
+
+ mSetPathQueue = new ArrayDeque<String>(Arrays.asList(dst.split("/")));
+
+ mProfileService.getPbapClient().setPhoneBookFolderRoot();
+ }
+
+ private void setPhonebookFolder(String folder) {
+ mProfileService.getPbapClient().setPhoneBookFolderDown(folder);
+ }
+
+ public void onClick_abort(View v) {
+ mProfileService.getPbapClient().abort();
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ProfileService.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ProfileService.java
new file mode 100644
index 0000000..79d6d23
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ProfileService.java
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+//import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothMasInstance;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.vcard.VCardEntry;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapBmessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapEventReport;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapMessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapCard;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapClient;
+import org.codeaurora.bluetooth.bttestapp.R;
+import org.codeaurora.bluetooth.bttestapp.services.IMapServiceCallback;
+import org.codeaurora.bluetooth.bttestapp.services.IPbapServiceCallback;
+import org.codeaurora.bluetooth.bttestapp.services.PbapAuthActivity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class ProfileService extends Service {
+
+ private final static String TAG = "ProfileService";
+
+ private static final int PBAP_AUTH_NOTIFICATION_ID = 10000;
+
+ public static final String ACTION_HFP_CONNECTION_STATE = "org.codeaurora.bluetooth.action.HFP_CONNECTION_STATE";
+
+ public static final String ACTION_PBAP_CONNECTION_STATE = "org.codeaurora.bluetooth.action.PBAP_CONNECTION_STATE";
+
+ public static final String ACTION_MAP_CONNECTION_STATE = "org.codeaurora.bluetooth.action.MAP_CONNECTION_STATE";
+
+ public static final String ACTION_MAP_NOTIFICATION_STATE = "org.codeaurora.bluetooth.action.MAP_NOTIFICATION_STATE";
+
+ public static final String EXTRA_CONNECTED = "org.codeaurora.bluetooth.extra.CONNECTED";
+
+ public static final String EXTRA_NOTIFICATION_STATE = "org.codeaurora.bluetooth.extra.NOTIFICATION_STATE";
+
+ public static final String PBAP_AUTH_ACTION_REQUEST = "org.codeaurora.bluetooth.PBAP_AUTH_ACTION_REQUEST";
+
+ public static final String PBAP_AUTH_ACTION_CANCEL = "org.codeaurora.bluetooth.PBAP_AUTH_ACTION_CANCEL";
+
+ public static final String PBAP_AUTH_ACTION_RESPONSE = "org.codeaurora.bluetooth.PBAP_AUTH_ACTION_RESPONSE";
+
+ public static final String PBAP_AUTH_ACTION_TIMEOUT = "org.codeaurora.bluetooth.PBAP_AUTH_ACTION_TIMEOUT";
+
+ public static final String PBAP_AUTH_EXTRA_KEY = "org.codeaurora.bluetooth.PBAP_AUTH_EXTRA_KEY";
+
+ public static final String ACTION_MAP_GET_MESSAGE = "org.codeaurora.bluetooth.action.MAP_GET_MESSAGE";
+
+ public static final String EXTRA_MAP_INSTANCE_ID = "org.codeaurora.bluetooth.extra.MAP_INSTANCE_ID";
+
+ public static final String EXTRA_MAP_MESSAGE_HANDLE = "org.codeaurora.bluetooth.extra.MAP_MESSAGE_HANDLE";
+
+ private BluetoothDevice mDevice = null;
+
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ //private BluetoothHandsfreeClient mHfpClient = null;
+
+ private BluetoothPbapClient mPbapClient = null;
+
+ private IPbapServiceCallback mPbapCallback = null;
+
+ private final PbapSessionData mPbapSessionData = new PbapSessionData();
+
+ private HashMap<Integer, BluetoothMasClient> mMapClients = null;
+
+ private HashMap<Integer, IMapServiceCallback> mMapCallbacks = null;
+
+ private HashMap<Integer, MapSessionData> mMapSessionData = null;
+
+ private MapNotificationSender mMapNotificationSender = null;
+
+ private boolean mIsBound = false;
+
+ private final IBinder mBinder = new LocalBinder();
+
+ class PbapSessionData {
+ ArrayList<VCardEntry> pullPhoneBook = null;
+ ArrayList<BluetoothPbapCard> pullVcardListing = null;
+ VCardEntry pullVcardEntry = null;
+ }
+
+ class MapSessionData {
+ ArrayList<String> getFolderListing;
+ ArrayList<BluetoothMapMessage> getMessagesListing;
+ BluetoothMapBmessage getMessage;
+ }
+
+ public class LocalBinder extends Binder {
+ ProfileService getService() {
+ return ProfileService.this;
+ }
+ }
+
+ private final Handler mPbapHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ Intent intent = null;
+
+ switch (msg.what) {
+ case BluetoothPbapClient.EVENT_SESSION_CONNECTED:
+ intent = new Intent(ACTION_PBAP_CONNECTION_STATE);
+ intent.putExtra(EXTRA_CONNECTED, true);
+ break;
+
+ case BluetoothPbapClient.EVENT_SESSION_DISCONNECTED:
+ intent = new Intent(ACTION_PBAP_CONNECTION_STATE);
+ intent.putExtra(EXTRA_CONNECTED, false);
+ break;
+ }
+
+ if (intent != null) {
+ ProfileService.this.sendBroadcast(intent);
+ }
+
+ switch (msg.what) {
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_DONE:
+ mPbapSessionData.pullPhoneBook = (ArrayList<VCardEntry>) msg.obj;
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_DONE:
+ mPbapSessionData.pullVcardListing = (ArrayList<BluetoothPbapCard>) msg.obj;
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_ENTRY_DONE:
+ mPbapSessionData.pullVcardEntry = (VCardEntry) msg.obj;
+ break;
+ }
+
+ if (mPbapCallback == null) {
+ return;
+ }
+
+ switch (msg.what) {
+ case BluetoothPbapClient.EVENT_SET_PHONE_BOOK_DONE:
+ mPbapCallback.onSetPhoneBookDone();
+ break;
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_DONE:
+ mPbapCallback.onPullPhoneBookDone(mPbapSessionData.pullPhoneBook, msg.arg1);
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_DONE:
+ mPbapCallback.onPullVcardListingDone(mPbapSessionData.pullVcardListing,
+ msg.arg1);
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_ENTRY_DONE:
+ mPbapCallback.onPullVcardEntryDone(mPbapSessionData.pullVcardEntry);
+ break;
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_SIZE_DONE:
+ mPbapCallback.onPullPhoneBookSizeDone(msg.arg1, 0);
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_SIZE_DONE:
+ mPbapCallback.onPullPhoneBookSizeDone(msg.arg1, 1);
+ break;
+ case BluetoothPbapClient.EVENT_SET_PHONE_BOOK_ERROR:
+ mPbapCallback.onSetPhoneBookError();
+ break;
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_ERROR:
+ mPbapCallback.onPullPhoneBookError();
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_ERROR:
+ mPbapCallback.onPullVcardListingError();
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_ENTRY_ERROR:
+ mPbapCallback.onPullVcardEntryError();
+ break;
+ case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_SIZE_ERROR:
+ mPbapCallback.onPullPhoneBookSizeError();
+ break;
+ case BluetoothPbapClient.EVENT_PULL_VCARD_LISTING_SIZE_ERROR:
+ mPbapCallback.onPullVcardListingSizeError();
+ break;
+ case BluetoothPbapClient.EVENT_SESSION_CONNECTED:
+ mPbapCallback.onSessionConnected();
+ break;
+ case BluetoothPbapClient.EVENT_SESSION_DISCONNECTED:
+ mPbapCallback.onSessionDisconnected();
+ break;
+ case BluetoothPbapClient.EVENT_SESSION_AUTH_REQUESTED:
+ createPbapAuthNotification();
+ break;
+ case BluetoothPbapClient.EVENT_SESSION_AUTH_TIMEOUT:
+ removePbapAuthNotification();
+ break;
+
+ default:
+ Log.w(TAG, "Unknown message in PBAP handler: " + msg.what);
+ break;
+ }
+ }
+ };
+
+ private final Handler mMapHandler = new Handler() {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void handleMessage(Message msg) {
+ IMapServiceCallback cb = mMapCallbacks.get(msg.arg2);
+
+ Log.v(TAG, "mMapHandler::handleMessage msg=" + msg.what + " status=" + msg.arg1
+ + " instanceid=" + msg.arg2);
+
+ boolean success = (msg.arg1 == BluetoothMasClient.STATUS_OK);
+
+ if (msg.what == BluetoothMasClient.EVENT_CONNECT) {
+ Intent intent = new Intent(ACTION_MAP_CONNECTION_STATE);
+ intent.putExtra(BluetoothDevice.EXTRA_MAS_INSTANCE, new BluetoothMasInstance(
+ msg.arg2, null, 0, 0));
+ intent.putExtra(EXTRA_CONNECTED, success);
+
+ ProfileService.this.sendBroadcast(intent);
+ } else if (msg.what == BluetoothMasClient.EVENT_SET_NOTIFICATION_REGISTRATION) {
+ Intent intent = new Intent(ACTION_MAP_NOTIFICATION_STATE);
+ intent.putExtra(BluetoothDevice.EXTRA_MAS_INSTANCE, new BluetoothMasInstance(
+ msg.arg2, null, 0, 0));
+ intent.putExtra(EXTRA_NOTIFICATION_STATE, ((Integer) msg.obj).intValue() != 0);
+
+ ProfileService.this.sendBroadcast(intent);
+ } else if (msg.what == BluetoothMasClient.EVENT_EVENT_REPORT) {
+ BluetoothMapEventReport evt = (BluetoothMapEventReport) msg.obj;
+ mMapNotificationSender.notify(msg.arg2, evt);
+ }
+
+ MapSessionData sessionData = mMapSessionData.get(msg.arg2);
+ if (sessionData == null) {
+ sessionData = new MapSessionData();
+ mMapSessionData.put(msg.arg2, sessionData);
+ }
+
+ if (success) {
+ switch (msg.what) {
+ case BluetoothMasClient.EVENT_GET_FOLDER_LISTING:
+ sessionData.getFolderListing = (ArrayList<String>) msg.obj;
+ break;
+ case BluetoothMasClient.EVENT_GET_MESSAGES_LISTING:
+ sessionData.getMessagesListing = (ArrayList<BluetoothMapMessage>) msg.obj;
+ break;
+ case BluetoothMasClient.EVENT_GET_MESSAGE:
+ sessionData.getMessage = (BluetoothMapBmessage) msg.obj;
+ break;
+ }
+ }
+
+ if (cb == null) {
+ Log.w(TAG, "No callback registered for MAS instance " + msg.arg2);
+ return;
+ }
+
+ switch (msg.what) {
+ case BluetoothMasClient.EVENT_CONNECT:
+ if (success) {
+ cb.onConnect();
+ } else {
+ cb.onConnectError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_UPDATE_INBOX:
+ if (success) {
+ cb.onUpdateInbox();
+ } else {
+ cb.onUpdateInboxError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_SET_PATH:
+ if (success) {
+ cb.onSetPath((String) msg.obj);
+ } else {
+ cb.onSetPathError((String) msg.obj);
+ }
+ break;
+ case BluetoothMasClient.EVENT_GET_FOLDER_LISTING:
+ if (success && msg.obj instanceof ArrayList<?>) {
+ cb.onGetFolderListing(sessionData.getFolderListing);
+ } else {
+ cb.onGetFolderListingError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_GET_FOLDER_LISTING_SIZE:
+ if (success && msg.obj instanceof Integer) {
+ cb.onGetFolderListingSize((Integer) msg.obj);
+ } else {
+ cb.onGetFolderListingSizeError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_GET_MESSAGES_LISTING:
+ if (success && msg.obj instanceof ArrayList<?>) {
+ cb.onGetMessagesListing(sessionData.getMessagesListing);
+ } else {
+ cb.onGetMessagesListingError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_GET_MESSAGE:
+ if (success && msg.obj instanceof BluetoothMapBmessage) {
+ cb.onGetMessage(sessionData.getMessage);
+ } else {
+ cb.onGetMessageError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_SET_MESSAGE_STATUS:
+ if (success) {
+ cb.onSetMessageStatus();
+ } else {
+ cb.onSetMessageStatusError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_PUSH_MESSAGE:
+ if (success && msg.obj instanceof String) {
+ cb.onPushMessage((String) msg.obj);
+ } else {
+ cb.onPushMessageError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_GET_MESSAGES_LISTING_SIZE:
+ if (success && msg.obj instanceof Integer) {
+ cb.onGetMessagesListingSize((Integer) msg.obj);
+ } else {
+ cb.onGetMessagesListingSizeError();
+ }
+ break;
+ case BluetoothMasClient.EVENT_EVENT_REPORT:
+ BluetoothMapEventReport evt = (BluetoothMapEventReport) msg.obj;
+ cb.onEventReport(evt);
+ break;
+ default:
+ Log.w(TAG, "Unknown message in MAP: " + msg.what);
+ }
+
+ }
+ };
+
+ public class MapNotificationSender {
+
+ public final int NEW_MESSAGE_NOTIFICATION_ID = 20000;
+
+ public final int DELIVERY_SUCCESS_NOTIFICATION_ID = 20001;
+
+ public final int SENDING_SUCCESS_NOTIFICATION_ID = 20002;
+
+ public final int DELIVERY_FAILURE_NOTIFICATION_ID = 20003;
+
+ public final int SENDING_FAILURE_NOTIFICATION_ID = 20004;
+
+ public final int MEMORY_FULL_NOTIFICATION_ID = 20005;
+
+ public final int MEMORY_AVAILABLE_NOTIFICATION_ID = 20006;
+
+ public final int MESSAGE_DELETED_NOTIFICATION_ID = 20007;
+
+ public final int MESSAGE_SHIFT_NOTIFICATION_ID = 20008;
+
+ private final NotificationManager mNotificationManager;
+
+ MapNotificationSender() {
+ mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ public void notify(int instanceId, BluetoothMapEventReport eventReport) {
+ switch (eventReport.getType()) {
+ case NEW_MESSAGE:
+ notifyNewMessage(instanceId, eventReport);
+ break;
+ case DELIVERY_SUCCESS:
+ notifyDeliverySuccess(eventReport);
+ break;
+ case SENDING_SUCCESS:
+ notifySendingSuccess(eventReport);
+ break;
+ case DELIVERY_FAILURE:
+ notifyDeliveryFailure(eventReport);
+ break;
+ case SENDING_FAILURE:
+ notifySendingFailure(eventReport);
+ break;
+ case MEMORY_AVAILABLE:
+ notifyMemoryAvailable(eventReport);
+ break;
+ case MEMORY_FULL:
+ notifyMemoryFull(eventReport);
+ break;
+ case MESSAGE_DELETED:
+ notifyMessageDeleted(eventReport);
+ break;
+ case MESSAGE_SHIFT:
+ notifyMessageShift(eventReport);
+ break;
+ default:
+ Log.e(TAG, "Unknown MAP report type (" + eventReport.getType().toString()
+ + ")!");
+ break;
+ }
+ }
+
+ private void send(int id, String title, String text) {
+ send(id, title, text, null);
+ }
+
+ private void send(int id, String title, String text, Intent click) {
+ Notification.Builder builder = new Notification.Builder(getApplicationContext())
+ .setContentTitle(title)
+ .setContentText(text)
+ .setTicker(getString(R.string.map_report_notif_ticker))
+ .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
+ .setDefaults(Notification.DEFAULT_SOUND)
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true);
+
+ if (click != null) {
+ builder.setContentIntent(PendingIntent.getActivity(ProfileService.this, 0, click,
+ PendingIntent.FLAG_UPDATE_CURRENT));
+ }
+
+ mNotificationManager.notify(id, builder.build());
+ }
+
+ private void notifyNewMessage(int instanceId, BluetoothMapEventReport eventReport) {
+ Intent click = new Intent(ProfileService.this, MapTestActivity.class);
+ click.setAction(ACTION_MAP_GET_MESSAGE);
+ click.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ click.putExtra(EXTRA_MAP_INSTANCE_ID, instanceId);
+ click.putExtra(EXTRA_MAP_MESSAGE_HANDLE, eventReport.getHandle());
+
+ send(NEW_MESSAGE_NOTIFICATION_ID,
+ String.format(getString(R.string.map_report_notif_received, eventReport
+ .getMsgType().toString())),
+ String.format(getString(R.string.map_report_notif_handle,
+ eventReport.getHandle())),
+ click);
+ }
+
+ private void notifyDeliverySuccess(BluetoothMapEventReport eventReport) {
+ send(DELIVERY_SUCCESS_NOTIFICATION_ID,
+ getString(R.string.map_report_notif_title_delivery_success),
+ String.format(getString(R.string.map_report_notif_handle,
+ eventReport.getHandle())));
+ }
+
+ private void notifySendingSuccess(BluetoothMapEventReport eventReport) {
+ send(SENDING_SUCCESS_NOTIFICATION_ID,
+ getString(R.string.map_report_notif_title_sending_success),
+ String.format(getString(R.string.map_report_notif_handle,
+ eventReport.getHandle())));
+ }
+
+ private void notifyDeliveryFailure(BluetoothMapEventReport eventReport) {
+ send(DELIVERY_FAILURE_NOTIFICATION_ID,
+ getString(R.string.map_report_notif_title_delivery_failure),
+ String.format(getString(R.string.map_report_notif_handle,
+ eventReport.getHandle())));
+ }
+
+ private void notifySendingFailure(BluetoothMapEventReport eventReport) {
+ send(SENDING_FAILURE_NOTIFICATION_ID,
+ getString(R.string.map_report_notif_title_sending_failure),
+ String.format(getString(R.string.map_report_notif_handle,
+ eventReport.getHandle())));
+ }
+
+ private void notifyMemoryFull(BluetoothMapEventReport eventReport) {
+ send(MEMORY_FULL_NOTIFICATION_ID,
+ getString(R.string.map_report_notif_title_memory_full),
+ getString(R.string.blank));
+ }
+
+ private void notifyMemoryAvailable(BluetoothMapEventReport eventReport) {
+ send(MEMORY_AVAILABLE_NOTIFICATION_ID,
+ getString(R.string.map_report_notif_title_memory_available),
+ getString(R.string.blank));
+ }
+
+ private void notifyMessageDeleted(BluetoothMapEventReport eventReport) {
+ send(MESSAGE_DELETED_NOTIFICATION_ID,
+ getString(R.string.map_report_notif_title_message_deleted),
+ String.format(getString(R.string.map_report_notif_handle,
+ eventReport.getHandle())));
+ }
+
+ private void notifyMessageShift(BluetoothMapEventReport eventReport) {
+ send(MESSAGE_SHIFT_NOTIFICATION_ID,
+ String.format(getString(R.string.map_report_notif_shifted,
+ eventReport.getHandle())),
+ String.format(getString(R.string.map_report_notif_fromto,
+ eventReport.getOldFolder(), eventReport.getFolder())));
+ }
+ }
+
+ BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.d(TAG, "received " + action);
+
+ /*if (BluetoothHandsfreeClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+ Intent new_intent = new Intent(ACTION_HFP_CONNECTION_STATE);
+
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ new_intent.putExtra(EXTRA_CONNECTED, true);
+ } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ new_intent.putExtra(EXTRA_CONNECTED, false);
+ } else {
+ return;
+ }
+
+ ProfileService.this.sendBroadcast(new_intent);
+ } else */if (PBAP_AUTH_ACTION_RESPONSE.equals(action)) {
+ String key = intent.getStringExtra(PBAP_AUTH_EXTRA_KEY);
+ mPbapClient.setAuthResponse(key);
+ } else if (PBAP_AUTH_ACTION_CANCEL.equals(action)) {
+ mPbapClient.setAuthResponse(null);
+ } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
+ BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ if (dev.equals(mDevice)) {
+ if (mPbapClient != null) {
+ mPbapClient.disconnect();
+ }
+
+ for (BluetoothMasClient cli : mMapClients.values()) {
+ cli.disconnect();
+ }
+
+ checkAndStop(false, true);
+ }
+ }
+ }
+ };
+
+/* private final ServiceListener mHfpServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HANDSFREE_CLIENT) {
+ mHfpClient = (BluetoothHandsfreeClient) proxy;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HANDSFREE_CLIENT) {
+ mHfpClient = null;
+ }
+ }
+ };
+*/
+ private void createPbapAuthNotification() {
+ NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+ Intent click = new Intent(this, PbapAuthActivity.class);
+ click.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ click.setAction(PBAP_AUTH_ACTION_REQUEST);
+ click.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+
+ Intent delete = new Intent(this, PbapAuthActivity.class);
+ delete.setAction(PBAP_AUTH_ACTION_CANCEL);
+
+ Notification no = new Notification.Builder(getApplicationContext())
+ .setContentTitle(getString(R.string.auth_notif_title))
+ .setContentText(getString(R.string.auth_notif_message, mDevice.getName()))
+ .setTicker(getString(R.string.auth_notif_ticker))
+ .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
+ .setContentIntent(PendingIntent.getActivity(this, 0, click, 0))
+ .setDeleteIntent(PendingIntent.getBroadcast(this, 0, delete, 0))
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true)
+ .setDefaults(Notification.DEFAULT_SOUND)
+ .build();
+
+ nm.notify(PBAP_AUTH_NOTIFICATION_ID, no);
+ }
+
+ private void removePbapAuthNotification() {
+ NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+ nm.cancel(PBAP_AUTH_NOTIFICATION_ID);
+
+ Intent intent = new Intent(PBAP_AUTH_ACTION_TIMEOUT);
+ sendBroadcast(intent);
+ }
+
+ private void checkAndStop(boolean unbind, boolean disconnect) {
+ boolean canStop = true;
+
+ Log.v(TAG, "checkAndStop(): unbind=" + unbind + " disconnect=" + disconnect);
+
+ if (unbind) {
+ /*if (mHfpClient != null &&
+ mHfpClient.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) {
+ canStop = false;
+ }*/
+
+ if (mPbapClient != null
+ && mPbapClient.getState() != BluetoothPbapClient.ConnectionState.DISCONNECTED) {
+ canStop = false;
+ }
+
+ for (BluetoothMasClient cli : mMapClients.values()) {
+ if (cli.getState() != BluetoothMasClient.ConnectionState.DISCONNECTED) {
+ canStop = false;
+ }
+ }
+
+ if (!canStop) {
+ Log.v(TAG, "clients are still connected, won't stop");
+ }
+ }
+
+ if (disconnect && mIsBound) {
+ canStop = false;
+ Log.v(TAG, "service is still bound, won't stop");
+ }
+
+ if (canStop) {
+ stopSelf();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ mIsBound = true;
+
+ return mBinder;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ mIsBound = false;
+
+ checkAndStop(true, false);
+
+ return false;
+ }
+
+ @Override
+ public void onCreate() {
+ Log.v(TAG, "onCreate");
+
+ mMapClients = new HashMap<Integer, BluetoothMasClient>();
+ mMapCallbacks = new HashMap<Integer, IMapServiceCallback>();
+ mMapSessionData = new HashMap<Integer, MapSessionData>();
+ mMapNotificationSender = new MapNotificationSender();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(PBAP_AUTH_ACTION_RESPONSE);
+ filter.addAction(PBAP_AUTH_ACTION_CANCEL);
+ filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ //filter.addAction(BluetoothHandsfreeClient.ACTION_CONNECTION_STATE_CHANGED);
+ registerReceiver(mReceiver, filter);
+
+// mAdapter.getProfileProxy(getApplicationContext(), mHfpServiceListener,
+ // BluetoothProfile.HANDSFREE_CLIENT);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.v(TAG, "onStartCommand intent=" + intent + " flags=" + Integer.toHexString(flags)
+ + " startId=" + startId);
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.v(TAG, "onDestroy");
+
+ //mAdapter.closeProfileProxy(BluetoothProfile.HANDSFREE_CLIENT,
+ // mHfpClient);
+
+ unregisterReceiver(mReceiver);
+
+ if (mPbapClient != null) {
+ mPbapClient.disconnect();
+ }
+
+ for (BluetoothMasClient cli : mMapClients.values()) {
+ cli.disconnect();
+ }
+ }
+
+ public void setDevice(BluetoothDevice device) {
+ if (mDevice != null && mDevice.equals(device)) {
+ return;
+ }
+
+ /*if (mHfpClient != null) {
+ mHfpClient.disconnect(mDevice);
+ }*/
+
+ if (mPbapClient != null) {
+ mPbapClient.disconnect();
+ }
+
+ for (BluetoothMasClient cli : mMapClients.values()) {
+ cli.disconnect();
+ }
+
+ mDevice = device;
+
+ if (mDevice != null) {
+ Log.v(TAG,
+ "Current device: address=" + mDevice.getAddress() + " name="
+ + mDevice.getName());
+ } else {
+ Log.v(TAG, "Current device: none");
+ }
+
+ mPbapClient = null;
+ mMapClients = new HashMap<Integer, BluetoothMasClient>();
+ mMapSessionData = new HashMap<Integer, MapSessionData>();
+ }
+
+ /*public BluetoothHandsfreeClient getHfpClient() {
+ return mHfpClient;
+ }*/
+
+ public BluetoothPbapClient getPbapClient() {
+ if (mDevice == null) {
+ return null;
+ }
+
+ if (mPbapClient == null) {
+ mPbapClient = new BluetoothPbapClient(mDevice, mPbapHandler);
+ }
+
+ return mPbapClient;
+ }
+
+ public void setPbapCallback(IPbapServiceCallback callback) {
+ mPbapCallback = callback;
+ }
+
+ public PbapSessionData getPbapSessionData() {
+ return mPbapSessionData;
+ }
+
+ public void setMasInstances(ArrayList<BluetoothMasInstance> instances) {
+ for (BluetoothMasInstance inst : instances) {
+ // no need to recreate already existing MAS client
+ if (mMapClients.containsKey(inst.getId())) {
+ continue;
+ }
+
+ BluetoothMasClient client = new BluetoothMasClient(mDevice, inst, mMapHandler);
+ mMapClients.put(inst.getId(), client);
+ }
+ }
+
+ public BluetoothMasClient getMapClient(int id) {
+ return mMapClients.get(id);
+ }
+
+ public void setMapCallback(int id, IMapServiceCallback callback) {
+ mMapCallbacks.put(id, callback);
+ }
+
+ public MapSessionData getMapSessionData(int id) {
+ return mMapSessionData.get(id);
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java
new file mode 100644
index 0000000..df72d43
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.app.ListFragment;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+//import android.bluetooth.BluetoothHandsfreeClient;
+import android.bluetooth.BluetoothMasInstance;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import org.codeaurora.bluetooth.mapclient.BluetoothMasClient;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapClient;
+import org.codeaurora.bluetooth.bttestapp.R;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public class ServicesFragment extends ListFragment {
+
+ private final static String TAG = "ServicesFragment";
+
+ private MainActivity mActivity;
+
+ private ServicesAdapter mAdapter;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ boolean connState = false;
+ boolean notifState = false;
+ boolean connEvent = false;
+ boolean notifEvent = false;
+ int idx = -1;
+
+ Log.v(TAG, "mReceiver got " + action);
+
+ /* if (ProfileService.ACTION_HFP_CONNECTION_STATE.equals(action)) {
+ connState = intent.getBooleanExtra(ProfileService.EXTRA_CONNECTED, false);
+
+ Service srv = new Service(Service.Type.HFP, null);
+ idx = mAdapter.getItemPos(srv);
+
+ if (idx < 0) {
+ Log.w(TAG, "Cannot find HFP service item");
+ }
+
+ connEvent = true;
+
+ } else */if (ProfileService.ACTION_PBAP_CONNECTION_STATE.equals(action)) {
+ connState = intent.getBooleanExtra(ProfileService.EXTRA_CONNECTED, false);
+
+ Service srv = new Service(Service.Type.PBAP, null);
+ idx = mAdapter.getItemPos(srv);
+
+ if (idx < 0) {
+ Log.w(TAG, "Cannot find PBAP service item");
+ }
+
+ connEvent = true;
+
+ } else if (ProfileService.ACTION_MAP_CONNECTION_STATE.equals(action)) {
+ connState = intent.getBooleanExtra(ProfileService.EXTRA_CONNECTED, false);
+ BluetoothMasInstance inst = intent
+ .getParcelableExtra(BluetoothDevice.EXTRA_MAS_INSTANCE);
+
+ Service srv = new Service(Service.Type.MAP, inst);
+ idx = mAdapter.getItemPos(srv);
+
+ if (idx < 0) {
+ Log.w(TAG, "Cannot find MAP service item");
+ }
+
+ connEvent = true;
+
+ } else if (ProfileService.ACTION_MAP_NOTIFICATION_STATE.equals(action)) {
+ notifState = intent.getBooleanExtra(ProfileService.EXTRA_NOTIFICATION_STATE, false);
+ BluetoothMasInstance inst = intent
+ .getParcelableExtra(BluetoothDevice.EXTRA_MAS_INSTANCE);
+
+ Service srv = new Service(Service.Type.MAP, inst);
+ idx = mAdapter.getItemPos(srv);
+
+ if (idx < 0) {
+ Log.w(TAG, "Cannot find MAP service item");
+ }
+
+ notifEvent = true;
+
+ }
+
+ if (idx < 0) {
+ return;
+ }
+
+ View v = (ServicesFragment.this).getListView().getChildAt(idx);
+ if (v != null) {
+ Switch swSrv = (Switch) v.findViewById(R.id.service_switch);
+ Switch swNotif = (Switch) v.findViewById(R.id.notification_switch);
+
+ if (connEvent) {
+ swSrv.setChecked(connState);
+ swSrv.setEnabled(true);
+ swNotif.setEnabled(connState);
+ swNotif.setChecked(false);
+ }
+ if (notifEvent) {
+ swNotif.setChecked(notifState);
+ swNotif.setEnabled(true);
+ }
+ }
+ }
+ };
+
+ static final class Service {
+
+ final Type mType;
+
+ final BluetoothMasInstance mMasInstance;
+
+ enum Type {
+ // HFP("Hands-Free Profile (AG)"),
+ PBAP("Phone Book Access Profile (PSE)"),
+ MAP("Message Access Profile (MSE)");
+
+ final String mTitle;
+
+ Type(String title) {
+ mTitle = title;
+ }
+ }
+
+ Service(Type type, BluetoothMasInstance masInstance) {
+ mType = type;
+ mMasInstance = masInstance;
+ }
+
+ @Override
+ public boolean equals(Object srv) {
+ if (!mType.equals(((Service) srv).mType)) {
+ return false;
+ }
+
+ if (mType.equals(Type.MAP)) {
+ return mMasInstance.equals(((Service) srv).mMasInstance);
+ }
+
+ return true;
+ }
+ }
+
+ private class ServicesAdapter extends BaseAdapter implements ListAdapter,
+ OnCheckedChangeListener {
+
+ private final LayoutInflater mInflater;
+
+ private final ArrayList<Service> mServices = new ArrayList<Service>();
+
+ public ServicesAdapter(Context context) {
+ super();
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public int getCount() {
+ return mServices.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mServices.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = convertView;
+ boolean bluetoothOn = BluetoothAdapter.getDefaultAdapter().isEnabled();
+
+ Log.v(TAG, "getView");
+ if (v == null) {
+ v = mInflater.inflate(R.layout.service_row, parent, false);
+ }
+
+ Service srv = mServices.get(position);
+
+ TextView txtName = (TextView) v.findViewById(R.id.service_name);
+ txtName.setText(srv.mType.mTitle);
+
+ TextView txtTitle = (TextView) v.findViewById(R.id.service_title);
+ if (srv.mType.equals(Service.Type.MAP)) {
+ txtTitle.setText(srv.mMasInstance.getName());
+ } else {
+ txtTitle.setText("");
+ }
+
+ Switch swSrv = (Switch) v.findViewById(R.id.service_switch);
+ swSrv.setTag(Integer.valueOf(position));
+ swSrv.setOnCheckedChangeListener(this);
+
+ Switch swNotif = (Switch) v.findViewById(R.id.notification_switch);
+ if (srv.mType.equals(Service.Type.MAP)) {
+ swNotif.setVisibility(View.VISIBLE);
+ swNotif.setTag(Integer.valueOf(position));
+ swNotif.setOnCheckedChangeListener(this);
+ } else {
+ swNotif.setVisibility(View.INVISIBLE);
+ }
+
+ // need to update switch state on new view
+ if (mActivity.mProfileService != null) {
+ switch (srv.mType) {
+ /* case HFP: {
+ BluetoothHandsfreeClient cli = mActivity.mProfileService.getHfpClient();
+
+ if (cli == null || bluetoothOn == false) {
+ swSrv.setChecked(false);
+ swSrv.setEnabled(false);
+ break;
+ }
+
+ switch (cli.getConnectionState(mActivity.mDevice)) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ swSrv.setChecked(false);
+ swSrv.setEnabled(true);
+ break;
+ case BluetoothProfile.STATE_CONNECTING:
+ swSrv.setChecked(true);
+ swSrv.setEnabled(false);
+ break;
+ case BluetoothProfile.STATE_CONNECTED:
+ swSrv.setChecked(true);
+ swSrv.setEnabled(true);
+ break;
+ case BluetoothProfile.STATE_DISCONNECTING:
+ swSrv.setChecked(false);
+ swSrv.setEnabled(false);
+ break;
+ }
+
+ break;
+ }
+*/
+ case PBAP: {
+ BluetoothPbapClient cli = mActivity.mProfileService.getPbapClient();
+
+ if (cli == null || bluetoothOn == false) {
+ swSrv.setChecked(false);
+ swSrv.setEnabled(false);
+ break;
+ }
+
+ switch (cli.getState()) {
+ case DISCONNECTED:
+ swSrv.setChecked(false);
+ swSrv.setEnabled(true);
+ break;
+ case CONNECTING:
+ swSrv.setChecked(true);
+ swSrv.setEnabled(false);
+ break;
+ case CONNECTED:
+ swSrv.setChecked(true);
+ swSrv.setEnabled(true);
+ break;
+ case DISCONNECTING:
+ swSrv.setChecked(false);
+ swSrv.setEnabled(false);
+ break;
+ }
+
+ break;
+ }
+
+ case MAP: {
+ BluetoothMasClient cli = mActivity.mProfileService
+ .getMapClient(srv.mMasInstance.getId());
+
+ swNotif.setEnabled(false);
+
+ if (cli == null || bluetoothOn == false) {
+ swSrv.setChecked(false);
+ swSrv.setEnabled(false);
+ break;
+ }
+
+ switch (cli.getState()) {
+ case DISCONNECTED:
+ swSrv.setChecked(false);
+ swSrv.setEnabled(true);
+ break;
+ case CONNECTING:
+ swSrv.setChecked(true);
+ swSrv.setEnabled(false);
+ break;
+ case CONNECTED:
+ swSrv.setChecked(true);
+ swSrv.setEnabled(true);
+ swNotif.setEnabled(true);
+ break;
+ case DISCONNECTING:
+ swSrv.setChecked(false);
+ swSrv.setEnabled(false);
+ break;
+ }
+
+ swNotif.setChecked(cli.getNotificationRegistration());
+
+ break;
+ }
+ }
+ }
+
+ return v;
+ }
+
+ public int getItemPos(Service srv) {
+ for (int i = 0; i < mServices.size(); i++) {
+ if (mServices.get(i).equals(srv)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public void addService(Service.Type type, BluetoothMasInstance masInstance) {
+ Service srv = new Service(type, masInstance);
+
+ if (!mServices.contains(srv)) {
+ mServices.add(srv);
+ }
+
+ notifyDataSetChanged();
+ }
+
+ public void removeService(Service.Type type) {
+ for (Iterator<Service> iter = mServices.iterator(); iter.hasNext();) {
+ Service srv = iter.next();
+
+ if (type == null || srv.mType.equals(type)) {
+ iter.remove();
+ }
+ }
+
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Service srv = mServices.get((Integer) buttonView.getTag());
+
+ switch (srv.mType) {
+/* case HFP:
+ if (isChecked) {
+ mActivity.mProfileService.getHfpClient().connect(mActivity.mDevice);
+ buttonView.setEnabled(false);
+ } else {
+ mActivity.mProfileService.getHfpClient().disconnect(mActivity.mDevice);
+ buttonView.setEnabled(false);
+ }
+ break;
+*/
+ case PBAP:
+ if (isChecked) {
+ mActivity.mProfileService.getPbapClient().connect();
+ buttonView.setEnabled(false);
+ } else {
+ mActivity.mProfileService.getPbapClient().disconnect();
+ buttonView.setEnabled(false);
+ }
+ break;
+
+ case MAP:
+ BluetoothMasClient cli = mActivity.mProfileService
+ .getMapClient(srv.mMasInstance.getId());
+ if (isChecked) {
+ if (buttonView.getId() == R.id.service_switch) {
+ cli.connect();
+ } else {
+ cli.setNotificationRegistration(true);
+ }
+ buttonView.setEnabled(false);
+ } else {
+ if (buttonView.getId() == R.id.service_switch) {
+ cli.disconnect();
+ } else {
+ cli.setNotificationRegistration(false);
+ }
+ buttonView.setEnabled(false);
+ }
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mAdapter = new ServicesAdapter(getActivity());
+ setListAdapter(mAdapter);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mActivity = (MainActivity) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(this.getClass().getName()
+ + " can only be attached to MainActivity!");
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ProfileService.ACTION_HFP_CONNECTION_STATE);
+ filter.addAction(ProfileService.ACTION_PBAP_CONNECTION_STATE);
+ filter.addAction(ProfileService.ACTION_MAP_CONNECTION_STATE);
+ filter.addAction(ProfileService.ACTION_MAP_NOTIFICATION_STATE);
+ getActivity().registerReceiver(mReceiver, filter);
+
+ mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ getActivity().unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public void onListItemClick(ListView lv, View v, int position, long id) {
+ Service srv = (Service) mAdapter.getItem(position);
+ Intent intent = null;
+
+ if (BluetoothAdapter.getDefaultAdapter().isEnabled() == false) return;
+
+ switch (srv.mType) {
+ case PBAP:
+ intent = new Intent(getActivity(), PbapTestActivity.class);
+ break;
+
+/* case HFP:
+ intent = new Intent(getActivity(), HfpTestActivity.class);
+ break;
+*/
+ case MAP:
+ intent = new Intent(getActivity(), MapTestActivity.class);
+ intent.putExtra(ProfileService.EXTRA_MAP_INSTANCE_ID, srv.mMasInstance.getId());
+ break;
+
+ default:
+ // this should never happen!
+ throw new IllegalArgumentException();
+ }
+
+ startActivity(intent);
+ }
+
+ public void addService(Service.Type type, BluetoothMasInstance masInstance) {
+ mAdapter.addService(type, masInstance);
+ }
+
+ public void removeService(Service.Type type) {
+ mAdapter.removeService(type);
+ }
+
+ public void persistServices() {
+ SharedPreferences.Editor prefs = getActivity().getPreferences(Context.MODE_PRIVATE).edit();
+
+ JSONArray json = new JSONArray();
+
+ int cnt = mAdapter.getCount();
+ for (int i = 0; i < cnt; i++) {
+ Service srv = (Service) mAdapter.getItem(i);
+
+ try {
+ JSONObject jsrv = new JSONObject();
+
+ jsrv.put("type", srv.mType);
+
+ if (srv.mMasInstance != null) {
+ jsrv.put("i_id", srv.mMasInstance.getId());
+ jsrv.put("i_name", srv.mMasInstance.getName());
+ jsrv.put("i_scn", srv.mMasInstance.getChannel());
+ jsrv.put("i_msg", srv.mMasInstance.getMsgTypes());
+ }
+
+ json.put(jsrv);
+ } catch (JSONException e) {
+ }
+ }
+
+ prefs.putString(MainActivity.PREF_SERVICES, json.toString());
+
+ prefs.commit();
+ }
+
+ public void restoreServices() {
+ String str = getActivity().getPreferences(Context.MODE_PRIVATE).getString(
+ MainActivity.PREF_SERVICES, null);
+
+ ArrayList<BluetoothMasInstance> insts = new ArrayList<BluetoothMasInstance>();
+
+ if (str == null) {
+ return;
+ }
+
+ try {
+ Object obj = new JSONTokener(str).nextValue();
+
+ if (!(obj instanceof JSONArray)) {
+ return;
+ }
+
+ JSONArray json = (JSONArray) obj;
+
+ for (int i = 0; i < json.length(); i++) {
+ JSONObject jsrv = json.getJSONObject(i);
+
+ String stype = jsrv.getString("type");
+ Service.Type type = null;
+ BluetoothMasInstance inst = null;
+
+ for (Service.Type t : Service.Type.values()) {
+ if (t.name().equals(stype)) {
+ type = t;
+ break;
+ }
+ }
+
+ if (type == null) {
+ continue;
+ }
+
+ if (type.equals(Service.Type.MAP)) {
+ inst = new BluetoothMasInstance(jsrv.getInt("i_id"), jsrv.getString("i_name"),
+ jsrv.getInt("i_scn"), jsrv.getInt("i_msg"));
+ insts.add(inst);
+ }
+
+ mAdapter.addService(type, inst);
+ }
+ } catch (JSONException e) {
+ }
+
+ mActivity.mProfileService.setMasInstances(insts);
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/StringListDialogFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/StringListDialogFragment.java
new file mode 100644
index 0000000..899ad7c
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/StringListDialogFragment.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+class StringListDialogFragment extends DialogFragment {
+
+ private final ArrayList<String> mElements;
+
+ private TreeSet<Integer> mSelectedItems;
+
+ private StringListDialogListener mListener;
+
+ public interface StringListDialogListener {
+ public void onStringListDialogPositive(DialogFragment dialog, ArrayList<String> elements);
+ };
+
+ public StringListDialogFragment(ArrayList<String> elements) {
+ super();
+
+ mElements = new ArrayList<String>(elements);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mSelectedItems = new TreeSet<Integer>();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ CharSequence[] elements = mElements.toArray(new CharSequence[mElements.size()]);
+
+ builder.setTitle("Select items to remove")
+ .setMultiChoiceItems(elements, null,
+ new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ if (isChecked) {
+ mSelectedItems.add(which);
+ } else if (mSelectedItems.contains(which)) {
+ mSelectedItems.remove(which);
+ }
+ }
+ })
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Iterator<Integer> iter = mSelectedItems.descendingIterator();
+
+ while (iter.hasNext()) {
+ int idx = iter.next();
+ mElements.remove(idx);
+ }
+
+ mListener.onStringListDialogPositive(StringListDialogFragment.this,
+ mElements);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mListener = (StringListDialogListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement StringListDialogListener");
+ }
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardFilterActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardFilterActivity.java
new file mode 100644
index 0000000..0c26762
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardFilterActivity.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.os.Bundle;
+
+import org.codeaurora.bluetooth.bttestapp.ProfileService;
+import org.codeaurora.bluetooth.bttestapp.util.Logger;
+
+public class VcardFilterActivity extends FilterActivity {
+
+ private final String TAG = "FilterChooserActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Logger.v(TAG, "onCreate()");
+ ActivityHelper.initialize(this, R.layout.activity_vcard_filter);
+ ActivityHelper.setActionBarTitle(this, R.string.title_vcard_filter);
+ }
+
+ @Override
+ protected void fillParameters() {
+ addParameter(0, R.id.filter_chooser_version);
+ addParameter(1, R.id.filter_chooser_fn);
+ addParameter(2, R.id.filter_chooser_n);
+ addParameter(3, R.id.filter_chooser_photo);
+ addParameter(4, R.id.filter_chooser_bday);
+ addParameter(5, R.id.filter_chooser_adr);
+ addParameter(6, R.id.filter_chooser_label);
+ addParameter(7, R.id.filter_chooser_tel);
+ addParameter(8, R.id.filter_chooser_email);
+ addParameter(9, R.id.filter_chooser_mailer);
+ addParameter(10, R.id.filter_chooser_tz);
+ addParameter(11, R.id.filter_chooser_geo);
+ addParameter(12, R.id.filter_chooser_title);
+ addParameter(13, R.id.filter_chooser_role);
+ addParameter(14, R.id.filter_chooser_logo);
+ addParameter(15, R.id.filter_chooser_agent);
+ addParameter(16, R.id.filter_chooser_org);
+ addParameter(17, R.id.filter_chooser_note);
+ addParameter(18, R.id.filter_chooser_rev);
+ addParameter(19, R.id.filter_chooser_sound);
+ addParameter(20, R.id.filter_chooser_url);
+ addParameter(21, R.id.filter_chooser_uid);
+ addParameter(22, R.id.filter_chooser_key);
+ addParameter(23, R.id.filter_chooser_nickname);
+ addParameter(24, R.id.filter_chooser_categories);
+ addParameter(25, R.id.filter_chooser_proid);
+ addParameter(26, R.id.filter_chooser_class);
+ addParameter(27, R.id.filter_chooser_sort_string);
+ addParameter(28, R.id.filter_chooser_x_irmc_call_datetime);
+ addParameter(39, R.id.filter_chooser_filter);
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardView.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardView.java
new file mode 100644
index 0000000..3ca4ded
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/VcardView.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.vcard.VCardEntry;
+import org.codeaurora.bluetooth.bttestapp.R;
+
+public class VcardView extends LinearLayout {
+
+ private final OnLongClickListener mLongClickListener = new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ String txt = ((TextView) v).getText().toString();
+
+ ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(
+ Context.CLIPBOARD_SERVICE);
+
+ clipboard.setPrimaryClip(ClipData.newPlainText(txt, txt));
+
+ Toast.makeText(getContext(), "copied: " + txt, Toast.LENGTH_SHORT).show();
+
+ return true;
+ }
+ };
+
+ public VcardView(Context context) {
+ super(context);
+ }
+
+ public VcardView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public VcardView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ private static VCardEntry mVCardEntry = null;
+
+ public void setVCardEntry(VCardEntry vcard) {
+ mVCardEntry = vcard;
+ populateUi();
+ }
+
+ private void addToContainer(ViewGroup cont, String str) {
+ TextView txt = new TextView(getContext());
+ txt.setText(str);
+ txt.setTextAppearance(getContext(), android.R.style.TextAppearance_Large);
+ txt.setPadding(3, 3, 3, 3);
+ txt.setOnLongClickListener(mLongClickListener);
+ cont.addView(txt);
+ }
+
+ private void populateUi() {
+ removeAllViewsInLayout();
+
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ View view = inflater.inflate(R.layout.vcard_view, this);
+
+ ((ImageView) view.findViewById(R.id.contact_photo))
+ .setImageResource(R.drawable.ic_contact_picture);
+
+ if (mVCardEntry.getPhotoList() != null && mVCardEntry.getPhotoList().size() > 0) {
+ byte[] data = mVCardEntry.getPhotoList().get(0).getBytes();
+ try {
+ if (data != null) {
+ Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
+ ((ImageView) view.findViewById(R.id.contact_photo)).setImageBitmap(bmp);
+ }
+ } catch (IllegalArgumentException e) {
+ // don't care, will just show dummy image
+ }
+ }
+
+ ((TextView) view.findViewById(R.id.contact_display_name)).setText(mVCardEntry
+ .getDisplayName());
+
+ if (mVCardEntry.getPhoneList() != null) {
+ LinearLayout cont = (LinearLayout) view.findViewById(R.id.contact_phonelist);
+
+ for (VCardEntry.PhoneData phone : mVCardEntry.getPhoneList()) {
+ addToContainer(cont, phone.getNumber());
+ }
+ }
+
+ if (mVCardEntry.getEmailList() != null) {
+ LinearLayout cont = (LinearLayout) view.findViewById(R.id.contact_emaillist);
+
+ for (VCardEntry.EmailData email : mVCardEntry.getEmailList()) {
+ addToContainer(cont, email.getAddress());
+ }
+ }
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IMapServiceCallback.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IMapServiceCallback.java
new file mode 100644
index 0000000..b1f4608
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IMapServiceCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp.services;
+
+import org.codeaurora.bluetooth.mapclient.BluetoothMapBmessage;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapEventReport;
+import org.codeaurora.bluetooth.mapclient.BluetoothMapMessage;
+
+import java.util.ArrayList;
+
+public interface IMapServiceCallback {
+
+ public void onConnect();
+
+ public void onConnectError();
+
+ public void onUpdateInbox();
+
+ public void onUpdateInboxError();
+
+ public void onSetPath(String currentPath);
+
+ public void onSetPathError(String currentPath);
+
+ public void onGetFolderListing(ArrayList<String> folders);
+
+ public void onGetFolderListingError();
+
+ public void onGetFolderListingSize(int size);
+
+ public void onGetFolderListingSizeError();
+
+ public void onGetMessagesListing(ArrayList<BluetoothMapMessage> messages);
+
+ public void onGetMessagesListingError();
+
+ public void onGetMessage(BluetoothMapBmessage message);
+
+ public void onGetMessageError();
+
+ public void onSetMessageStatus();
+
+ public void onSetMessageStatusError();
+
+ public void onPushMessage(String handle);
+
+ public void onPushMessageError();
+
+ public void onGetMessagesListingSize(int size);
+
+ public void onGetMessagesListingSizeError();
+
+ public void onEventReport(BluetoothMapEventReport eventReport);
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IPbapServiceCallback.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IPbapServiceCallback.java
new file mode 100644
index 0000000..292ecc5
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/IPbapServiceCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp.services;
+
+import com.android.vcard.VCardEntry;
+import org.codeaurora.bluetooth.pbapclient.BluetoothPbapCard;
+
+import java.util.ArrayList;
+
+public interface IPbapServiceCallback {
+
+ void onSetPhoneBookDone();
+
+ void onPullPhoneBookDone(ArrayList<VCardEntry> list, int missedCalls);
+
+ void onPullVcardListingDone(ArrayList<BluetoothPbapCard> list, int missedCalls);
+
+ void onPullVcardEntryDone(VCardEntry vcard);
+
+ void onPullPhoneBookSizeDone(int size, int type);
+
+ void onSetPhoneBookError();
+
+ void onPullPhoneBookError();
+
+ void onPullVcardListingError();
+
+ void onPullVcardEntryError();
+
+ void onPullPhoneBookSizeError();
+
+ void onPullVcardListingSizeError();
+
+ void onSessionConnected();
+
+ void onSessionDisconnected();
+
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/PbapAuthActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/PbapAuthActivity.java
new file mode 100644
index 0000000..4541c31
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/services/PbapAuthActivity.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.bttestapp.services;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import org.codeaurora.bluetooth.bttestapp.ProfileService;
+import org.codeaurora.bluetooth.bttestapp.R;
+
+public class PbapAuthActivity extends AlertActivity implements DialogInterface.OnClickListener,
+ TextWatcher {
+
+ private static final String TAG = "PbapAuthActivity";
+
+ private static final int KEY_MAX_LENGTH = 16;
+
+ private static final int TIMEOUT_MSG = 0;
+
+ private static final int TIMEOUT_VALUE_MS = 2000;
+
+ private static final String KEY_USER_TIMEOUT = "user_timeout";
+
+ private BluetoothDevice mDevice;
+
+ private Button mOkButton;
+
+ private EditText mKeyEditText;
+
+ private TextView mMessageTextView;
+
+ private String mKey;
+
+ private boolean mTimeout = false;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ProfileService.PBAP_AUTH_ACTION_TIMEOUT.equals(intent.getAction())) {
+ onTimeout();
+ }
+ }
+ };
+
+ private final Handler mTimeoutHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case TIMEOUT_MSG:
+ finish();
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+
+ if (ProfileService.PBAP_AUTH_ACTION_REQUEST.equals(intent.getAction())) {
+ mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ showAuthDialog();
+
+ registerReceiver(mReceiver, new IntentFilter(ProfileService.PBAP_AUTH_ACTION_TIMEOUT));
+ } else {
+ Log.e(TAG, "This activity can be also started with AUTH_ACTION_REQUEST intent");
+ finish();
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ mKey = mKeyEditText.getText().toString();
+ onPositive();
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ onNegative();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+
+ mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT);
+
+ if (mTimeout) {
+ onTimeout();
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putBoolean(KEY_USER_TIMEOUT, mTimeout);
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 0) {
+ mOkButton.setEnabled(true);
+ }
+ }
+
+ private void showAuthDialog() {
+ final AlertController.AlertParams p = mAlertParams;
+
+ p.mIconId = android.R.drawable.ic_dialog_info;
+ p.mTitle = getString(R.string.pbap_session_key_dialog_header);
+
+ p.mPositiveButtonText = getString(android.R.string.ok);
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = getString(android.R.string.cancel);
+ p.mNegativeButtonListener = this;
+
+ p.mView = createView();
+
+ setupAlert();
+
+ mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+ mOkButton.setEnabled(false);
+ }
+
+ private View createView() {
+ View view = getLayoutInflater().inflate(R.layout.pbap_auth, null);
+
+ mMessageTextView = (TextView) view.findViewById(R.id.message);
+ mMessageTextView.setText(getString(R.string.pbap_session_key_dialog_title,
+ mDevice.getName()));
+
+ mKeyEditText = (EditText) view.findViewById(R.id.text);
+ mKeyEditText.addTextChangedListener(this);
+ mKeyEditText.setFilters(new InputFilter[] {
+ new InputFilter.LengthFilter(KEY_MAX_LENGTH)
+ });
+
+ return view;
+ }
+
+ private void onTimeout() {
+ mTimeout = true;
+
+ mMessageTextView.setText(getString(R.string.pbap_authentication_timeout_message,
+ mDevice.getName()));
+
+ mKeyEditText.setVisibility(View.GONE);
+ mKeyEditText.clearFocus();
+ mKeyEditText.removeTextChangedListener(PbapAuthActivity.this);
+
+ mOkButton.setEnabled(true);
+
+ mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+
+ mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(TIMEOUT_MSG),
+ TIMEOUT_VALUE_MS);
+ }
+
+ private void onPositive() {
+ if (!mTimeout) {
+ Intent intent = new Intent(ProfileService.PBAP_AUTH_ACTION_RESPONSE);
+ intent.putExtra(ProfileService.PBAP_AUTH_EXTRA_KEY, mKey);
+ sendBroadcast(intent);
+
+ mKeyEditText.removeTextChangedListener(this);
+ }
+
+ mTimeout = false;
+ finish();
+ }
+
+ private void onNegative() {
+ Intent intent = new Intent(ProfileService.PBAP_AUTH_ACTION_CANCEL);
+ sendBroadcast(intent);
+
+ mKeyEditText.removeTextChangedListener(this);
+
+ finish();
+ }
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/Logger.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/Logger.java
new file mode 100644
index 0000000..26155cc
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/Logger.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp.util;
+
+import android.util.Log;
+
+public class Logger {
+
+ // Android Tag for logging.
+ private static final String TAG = "NexusCarKit";
+
+ private static final boolean DEBUG = true;
+
+ private static final boolean VLOG = DEBUG && true;
+ private static final boolean DLOG = DEBUG && true;
+ private static final boolean ILOG = DEBUG && true;
+ private static final boolean WLOG = DEBUG && true;
+ private static final boolean ELOG = DEBUG && true;
+
+ /**
+ * Constructor.
+ */
+ private Logger() {
+ }
+
+ public static void v(String tag, String msg) {
+ if (VLOG) {
+ Log.v(TAG, "[" + tag + "] " + msg);
+ }
+ }
+
+ public static void d(String tag, String msg) {
+ if (DLOG) {
+ Log.d(TAG, "[" + tag + "] " + msg);
+ }
+ }
+
+ public static void i(String tag, String msg) {
+ if (ILOG) {
+ Log.i(TAG, "[" + tag + "] " + msg);
+ }
+ }
+
+ public static void w(String tag, String msg) {
+ if (WLOG) {
+ Log.w(TAG, "[" + tag + "] " + msg);
+ }
+ }
+
+ public static void e(String tag, String msg) {
+ if (ELOG) {
+ Log.e(TAG, "[" + tag + "] " + msg);
+ }
+ }
+
+}
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/MonkeyEvent.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/MonkeyEvent.java
new file mode 100644
index 0000000..a3ee825
--- /dev/null
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/util/MonkeyEvent.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.bttestapp.util;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MonkeyEvent {
+
+ private static final String TAG = "MonkeyEventStream";
+
+ private static final String SEP = "~~";
+
+ private final String mName;
+
+ private final boolean mPass;
+
+ private HashMap<String, String> mParams = null;
+
+ private ArrayList<String> mExtReply = null;
+
+ public MonkeyEvent(String name, boolean pass) {
+ mName = name.toLowerCase();
+ mPass = pass;
+ mParams = new HashMap<String, String>();
+ }
+
+ public MonkeyEvent addReplyParam(String name, int value) {
+ addReplyParam(name, Integer.toString(value));
+ return this;
+ }
+
+ public MonkeyEvent addReplyParam(String name, long value) {
+ addReplyParam(name, Long.toString(value));
+ return this;
+ }
+
+ public MonkeyEvent addReplyParam(String name, String value) {
+ name = name.toLowerCase();
+ mParams.put(name, value);
+ return this;
+ }
+
+ public MonkeyEvent addExtReply(String s) {
+ if (s != null) {
+ if (mExtReply == null) {
+ mExtReply = new ArrayList<String>();
+ }
+
+ mExtReply.add(s);
+ }
+
+ return this;
+ }
+
+ public MonkeyEvent addExtReply(Collection<?> p) {
+ if (p != null && p.size() != 0) {
+ if (mExtReply == null) {
+ mExtReply = new ArrayList<String>();
+ }
+
+ for (Object obj : p.toArray()) {
+ mExtReply.add(obj.toString());
+ }
+ }
+
+ return this;
+ }
+
+ public void send() {
+ StringBuilder sb = new StringBuilder();
+
+ if (mExtReply == null) {
+ sb.append("EVENT");
+ } else {
+ sb.append("BEGINEXTEVENT");
+ }
+
+ sb.append(SEP);
+ sb.append(mName);
+ sb.append(SEP);
+ sb.append(mPass ? 1 : 0);
+
+ for (Map.Entry<String, String> entry : mParams.entrySet()) {
+ sb.append(SEP);
+ sb.append(entry.getKey());
+ sb.append("=");
+ sb.append(entry.getValue());
+ }
+
+ Log.i(TAG, sb.toString());
+
+ if (mExtReply != null) {
+ for (String s : mExtReply) {
+ Log.i(TAG, s);
+ }
+
+ Log.i(TAG, "ENDEXTEVENT");
+ }
+ }
+}