diff options
author | Keith Packard <keithp@keithp.com> | 2011-12-31 16:59:18 -0800 |
---|---|---|
committer | Keith Packard <keithp@keithp.com> | 2011-12-31 16:59:18 -0800 |
commit | ddbfa4f1e01a74f625a6a553f0261c6b2ad4fdd8 (patch) | |
tree | dcf31f0cb0e83d1f9fd5f840ef90aa5059bf1015 |
import
-rw-r--r-- | .classpath | 7 | ||||
-rw-r--r-- | .project | 33 | ||||
-rw-r--r-- | .settings/org.eclipse.jdt.core.prefs | 5 | ||||
-rw-r--r-- | AndroidManifest.xml | 38 | ||||
-rw-r--r-- | Makefile | 30 | ||||
-rw-r--r-- | bin/AltosDroid.apk | bin | 0 -> 25740 bytes | |||
-rw-r--r-- | bin/classes.dex | bin | 0 -> 22564 bytes | |||
-rw-r--r-- | bin/resources.ap_ | bin | 0 -> 12887 bytes | |||
-rw-r--r-- | default.properties | 11 | ||||
-rw-r--r-- | gen/org/altusmetrum/AltosDroid/R.java | 66 | ||||
-rw-r--r-- | keystore | bin | 0 -> 1270 bytes | |||
-rw-r--r-- | res/drawable-hdpi/app_icon.png | bin | 0 -> 5589 bytes | |||
-rw-r--r-- | res/drawable/app_icon.png | bin | 0 -> 4182 bytes | |||
-rw-r--r-- | res/layout/custom_title.xml | 39 | ||||
-rw-r--r-- | res/layout/device_list.xml | 56 | ||||
-rw-r--r-- | res/layout/device_name.xml | 21 | ||||
-rw-r--r-- | res/layout/main.xml | 46 | ||||
-rw-r--r-- | res/layout/message.xml | 21 | ||||
-rw-r--r-- | res/menu/option_menu.xml | 26 | ||||
-rw-r--r-- | res/values/strings.xml | 41 | ||||
-rw-r--r-- | src/org/altusmetrum/AltosDroid/AltosDroid.java | 357 | ||||
-rw-r--r-- | src/org/altusmetrum/AltosDroid/BluetoothChatService.java | 370 | ||||
-rw-r--r-- | src/org/altusmetrum/AltosDroid/DeviceListActivity.java | 203 |
23 files changed, 1370 insertions, 0 deletions
diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..6efcbb7 --- /dev/null +++ b/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="gen"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/.project b/.project new file mode 100644 index 0000000..7b56596 --- /dev/null +++ b/.project @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>AltosDroid</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>com.android.ide.eclipse.adt.ApkBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>com.android.ide.eclipse.adt.AndroidNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..e5d1cd3 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +#Wed Sep 28 19:51:24 NZDT 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000..b72f038 --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.altusmetrum.AltosDroid" + android:versionCode="1" + android:versionName="1.0"> + <uses-sdk minSdkVersion="6" /> + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> + <uses-permission android:name="android.permission.BLUETOOTH" /> + + <application android:label="@string/app_name" + android:icon="@drawable/app_icon" > + <activity android:label="@string/app_name" + android:configChanges="orientation|keyboardHidden" android:name="org.altusmetrum.AltosDroid.AltosDroid"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name=".DeviceListActivity" + android:label="@string/select_device" + android:theme="@android:style/Theme.Dialog" + android:configChanges="orientation|keyboardHidden" /> + </application> +</manifest> diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..577fe1b --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +SDK=/home/keithp/src/android/android-sdk-linux +DX=$(SDK)/platform-tools/dx +ADB=$(SDK)/platform-tools/adb +PLATFORM=$(SDK)/platforms/android-10 +TOOLS_LIB=$(SDK)/tools/lib +CLASSPATH=".:$(PLATFORM)/*:$(TOOLS_LIB)/*" +CLASSES=classes +CLASSPATH_ENV=mkdir $(CLASSES); CLASSPATH=$(CLASSPATH) +SRC_DIR=src/org/altusmetrum/AltosDroid +GEN_DIR=gen/org/altusmetrum/AltosDroid +JAVAC=javac +JAVACFLAGS=-d $(CLASSES) + +SRC=\ + $(SRC_DIR)/AltosDroid.java \ + $(SRC_DIR)/BluetoothChatService.java \ + $(SRC_DIR)/DeviceListActivity.java \ + $(GEN_DIR)/R.java + +all: class.stamp AltosDroid.apk + +class.stamp: $(SRC) + $(CLASSPATH_ENV) $(JAVAC) $(JAVACFLAGS) $(SRC) && touch class.stamp + +AltosDroid.apk: class.stamp + $(DX) --dex --verbose --output=$@ AndroidManifest.xml $(CLASSES) && \ + jarsigner -keystore ./keystore -storepass altusmetrum $@ bob + +install: AltosDroid.apk + $(ADB) -e install -r AltosDroid.apk diff --git a/bin/AltosDroid.apk b/bin/AltosDroid.apk Binary files differnew file mode 100644 index 0000000..56e7bb9 --- /dev/null +++ b/bin/AltosDroid.apk diff --git a/bin/classes.dex b/bin/classes.dex Binary files differnew file mode 100644 index 0000000..f53c2a0 --- /dev/null +++ b/bin/classes.dex diff --git a/bin/resources.ap_ b/bin/resources.ap_ Binary files differnew file mode 100644 index 0000000..cf01206 --- /dev/null +++ b/bin/resources.ap_ diff --git a/default.properties b/default.properties new file mode 100644 index 0000000..66db0d1 --- /dev/null +++ b/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-10 diff --git a/gen/org/altusmetrum/AltosDroid/R.java b/gen/org/altusmetrum/AltosDroid/R.java new file mode 100644 index 0000000..66f3449 --- /dev/null +++ b/gen/org/altusmetrum/AltosDroid/R.java @@ -0,0 +1,66 @@ +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package org.altusmetrum.AltosDroid; + +public final class R { + public static final class attr { + } + public static final class drawable { + public static final int app_icon=0x7f020000; + } + public static final class id { + public static final int button_scan=0x7f060006; + public static final int button_send=0x7f060009; + public static final int discoverable=0x7f06000c; + public static final int edit_text_out=0x7f060008; + public static final int in=0x7f060007; + public static final int insecure_connect_scan=0x7f06000b; + public static final int new_devices=0x7f060005; + public static final int paired_devices=0x7f060003; + public static final int secure_connect_scan=0x7f06000a; + public static final int title_left_text=0x7f060000; + public static final int title_new_devices=0x7f060004; + public static final int title_paired_devices=0x7f060002; + public static final int title_right_text=0x7f060001; + } + public static final class layout { + public static final int custom_title=0x7f030000; + public static final int device_list=0x7f030001; + public static final int device_name=0x7f030002; + public static final int main=0x7f030003; + public static final int message=0x7f030004; + } + public static final class menu { + public static final int option_menu=0x7f050000; + } + public static final class string { + public static final int app_name=0x7f040000; + public static final int bt_not_enabled_leaving=0x7f040003; + public static final int button_scan=0x7f04000d; + public static final int discoverable=0x7f040010; + public static final int insecure_connect=0x7f04000f; + public static final int none_found=0x7f04000a; + public static final int none_paired=0x7f040009; + public static final int not_connected=0x7f040002; + /** DeviceListActivity + */ + public static final int scanning=0x7f040007; + /** Options Menu + */ + public static final int secure_connect=0x7f04000e; + public static final int select_device=0x7f040008; + /** BluetoothChat + */ + public static final int send=0x7f040001; + public static final int title_connected_to=0x7f040005; + public static final int title_connecting=0x7f040004; + public static final int title_not_connected=0x7f040006; + public static final int title_other_devices=0x7f04000c; + public static final int title_paired_devices=0x7f04000b; + } +} diff --git a/keystore b/keystore Binary files differnew file mode 100644 index 0000000..00739d0 --- /dev/null +++ b/keystore diff --git a/res/drawable-hdpi/app_icon.png b/res/drawable-hdpi/app_icon.png Binary files differnew file mode 100644 index 0000000..8836ff6 --- /dev/null +++ b/res/drawable-hdpi/app_icon.png diff --git a/res/drawable/app_icon.png b/res/drawable/app_icon.png Binary files differnew file mode 100644 index 0000000..d3e09a5 --- /dev/null +++ b/res/drawable/app_icon.png diff --git a/res/layout/custom_title.xml b/res/layout/custom_title.xml new file mode 100644 index 0000000..57eb6b4 --- /dev/null +++ b/res/layout/custom_title.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical" + > + <TextView android:id="@+id/title_left_text" + android:layout_alignParentLeft="true" + android:ellipsize="end" + android:singleLine="true" + style="?android:attr/windowTitleStyle" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_weight="1" + /> + <TextView android:id="@+id/title_right_text" + android:layout_alignParentRight="true" + android:ellipsize="end" + android:singleLine="true" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:textColor="#fff" + android:layout_weight="1" + /> +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/device_list.xml b/res/layout/device_list.xml new file mode 100644 index 0000000..395695f --- /dev/null +++ b/res/layout/device_list.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <TextView android:id="@+id/title_paired_devices" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/title_paired_devices" + android:visibility="gone" + android:background="#666" + android:textColor="#fff" + android:paddingLeft="5dp" + /> + <ListView android:id="@+id/paired_devices" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:stackFromBottom="true" + android:layout_weight="1" + /> + <TextView android:id="@+id/title_new_devices" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/title_other_devices" + android:visibility="gone" + android:background="#666" + android:textColor="#fff" + android:paddingLeft="5dp" + /> + <ListView android:id="@+id/new_devices" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:stackFromBottom="true" + android:layout_weight="2" + /> + <Button android:id="@+id/button_scan" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/button_scan" + /> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/device_name.xml b/res/layout/device_name.xml new file mode 100644 index 0000000..8fa358c --- /dev/null +++ b/res/layout/device_name.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:padding="5dp" +/>
\ No newline at end of file diff --git a/res/layout/main.xml b/res/layout/main.xml new file mode 100644 index 0000000..17025f6 --- /dev/null +++ b/res/layout/main.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <ListView android:id="@+id/in" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:stackFromBottom="true" + android:transcriptMode="alwaysScroll" + android:layout_weight="1" + /> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + <EditText android:id="@+id/edit_text_out" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_gravity="bottom" + /> + <Button android:id="@+id/button_send" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/send" + /> + </LinearLayout> +</LinearLayout> diff --git a/res/layout/message.xml b/res/layout/message.xml new file mode 100644 index 0000000..8fa358c --- /dev/null +++ b/res/layout/message.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:padding="5dp" +/>
\ No newline at end of file diff --git a/res/menu/option_menu.xml b/res/menu/option_menu.xml new file mode 100644 index 0000000..18de725 --- /dev/null +++ b/res/menu/option_menu.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/secure_connect_scan" + android:icon="@android:drawable/ic_menu_search" + android:title="@string/secure_connect" /> + <item android:id="@+id/insecure_connect_scan" + android:icon="@android:drawable/ic_menu_search" + android:title="@string/insecure_connect" /> + <item android:id="@+id/discoverable" + android:icon="@android:drawable/ic_menu_mylocation" + android:title="@string/discoverable" /> +</menu> diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 0000000..53af2a3 --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> + +<resources> + <string name="app_name">Bluetooth Chat</string> + + <!-- BluetoothChat --> + <string name="send">Send</string> + <string name="not_connected">You are not connected to a device</string> + <string name="bt_not_enabled_leaving">Bluetooth was not enabled. Leaving Bluetooth Chat.</string> + <string name="title_connecting">connecting...</string> + <string name="title_connected_to">connected: </string> + <string name="title_not_connected">not connected</string> + + <!-- DeviceListActivity --> + <string name="scanning">scanning for devices...</string> + <string name="select_device">select a device to connect</string> + <string name="none_paired">No devices have been paired</string> + <string name="none_found">No devices found</string> + <string name="title_paired_devices">Paired Devices</string> + <string name="title_other_devices">Other Available Devices</string> + <string name="button_scan">Scan for devices</string> + + <!-- Options Menu --> + <string name="secure_connect">Connect a device - Secure</string> + <string name="insecure_connect">Connect a device - Insecure</string> + <string name="discoverable">Make discoverable</string> +</resources> diff --git a/src/org/altusmetrum/AltosDroid/AltosDroid.java b/src/org/altusmetrum/AltosDroid/AltosDroid.java new file mode 100644 index 0000000..844ca39 --- /dev/null +++ b/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -0,0 +1,357 @@ +/* + * Copyright (C) 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. + */ + +package org.altusmetrum.AltosDroid; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.view.View.OnClickListener; +import android.view.inputmethod.EditorInfo; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; +import org.altusmetrum.AltosDroid.R; + +/** + * This is the main Activity that displays the current chat session. + */ +public class AltosDroid extends Activity { + // Debugging + private static final String TAG = "BluetoothChat"; + private static final boolean D = true; + + // Message types sent from the BluetoothChatService Handler + public static final int MESSAGE_STATE_CHANGE = 1; + public static final int MESSAGE_READ = 2; + public static final int MESSAGE_WRITE = 3; + public static final int MESSAGE_DEVICE_NAME = 4; + public static final int MESSAGE_TOAST = 5; + + // Key names received from the BluetoothChatService Handler + public static final String DEVICE_NAME = "device_name"; + public static final String TOAST = "toast"; + + // Intent request codes + private static final int REQUEST_CONNECT_DEVICE_SECURE = 1; + private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2; + private static final int REQUEST_ENABLE_BT = 3; + + // Layout Views + private TextView mTitle; + private ListView mConversationView; + private EditText mOutEditText; + private Button mSendButton; + + // Name of the connected device + private String mConnectedDeviceName = null; + // Array adapter for the conversation thread + private ArrayAdapter<String> mConversationArrayAdapter; + // String buffer for outgoing messages + private StringBuffer mOutStringBuffer; + // Local Bluetooth adapter + private BluetoothAdapter mBluetoothAdapter = null; + // Member object for the chat services + private BluetoothChatService mChatService = null; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if(D) Log.e(TAG, "+++ ON CREATE +++"); + + // Set up the window layout + requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); + setContentView(R.layout.main); + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title); + + // Set up the custom title + mTitle = (TextView) findViewById(R.id.title_left_text); + mTitle.setText(R.string.app_name); + mTitle = (TextView) findViewById(R.id.title_right_text); + + // Get local Bluetooth adapter + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + + // If the adapter is null, then Bluetooth is not supported + if (mBluetoothAdapter == null) { + Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); + finish(); + return; + } + } + + @Override + public void onStart() { + super.onStart(); + if(D) Log.e(TAG, "++ ON START ++"); + + // If BT is not on, request that it be enabled. + // setupChat() will then be called during onActivityResult + if (!mBluetoothAdapter.isEnabled()) { + Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableIntent, REQUEST_ENABLE_BT); + // Otherwise, setup the chat session + } else { + if (mChatService == null) setupChat(); + } + } + + @Override + public synchronized void onResume() { + super.onResume(); + if(D) Log.e(TAG, "+ ON RESUME +"); + + // Performing this check in onResume() covers the case in which BT was + // not enabled during onStart(), so we were paused to enable it... + // onResume() will be called when ACTION_REQUEST_ENABLE activity returns. + if (mChatService != null) { + // Only if the state is STATE_NONE, do we know that we haven't started already + if (mChatService.getState() == BluetoothChatService.STATE_NONE) { + // Start the Bluetooth chat services + mChatService.start(); + } + } + } + + private void setupChat() { + Log.d(TAG, "setupChat()"); + + // Initialize the array adapter for the conversation thread + mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message); + mConversationView = (ListView) findViewById(R.id.in); + mConversationView.setAdapter(mConversationArrayAdapter); + + // Initialize the compose field with a listener for the return key + mOutEditText = (EditText) findViewById(R.id.edit_text_out); + mOutEditText.setOnEditorActionListener(mWriteListener); + + // Initialize the send button with a listener that for click events + mSendButton = (Button) findViewById(R.id.button_send); + mSendButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + // Send a message using content of the edit text widget + TextView view = (TextView) findViewById(R.id.edit_text_out); + String message = view.getText().toString(); + sendMessage(message); + } + }); + + // Initialize the BluetoothChatService to perform bluetooth connections + mChatService = new BluetoothChatService(this, mHandler); + + // Initialize the buffer for outgoing messages + mOutStringBuffer = new StringBuffer(""); + } + + @Override + public synchronized void onPause() { + super.onPause(); + if(D) Log.e(TAG, "- ON PAUSE -"); + } + + @Override + public void onStop() { + super.onStop(); + if(D) Log.e(TAG, "-- ON STOP --"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + // Stop the Bluetooth chat services + if (mChatService != null) mChatService.stop(); + if(D) Log.e(TAG, "--- ON DESTROY ---"); + } + + private void ensureDiscoverable() { + if(D) Log.d(TAG, "ensure discoverable"); + if (mBluetoothAdapter.getScanMode() != + BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); + discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); + startActivity(discoverableIntent); + } + } + + /** + * Sends a message. + * @param message A string of text to send. + */ + private void sendMessage(String message) { + // Check that we're actually connected before trying anything + if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) { + Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show(); + return; + } + + // Check that there's actually something to send + if (message.length() > 0) { + // Get the message bytes and tell the BluetoothChatService to write + byte[] send = message.getBytes(); + mChatService.write(send); + + // Reset out string buffer to zero and clear the edit text field + mOutStringBuffer.setLength(0); + mOutEditText.setText(mOutStringBuffer); + } + } + + // The action listener for the EditText widget, to listen for the return key + private TextView.OnEditorActionListener mWriteListener = + new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { + // If the action is a key-up event on the return key, send the message + if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) { + String message = view.getText().toString(); + sendMessage(message); + } + if(D) Log.i(TAG, "END onEditorAction"); + return true; + } + }; + + // The Handler that gets information back from the BluetoothChatService + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_STATE_CHANGE: + if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1); + switch (msg.arg1) { + case BluetoothChatService.STATE_CONNECTED: + mTitle.setText(R.string.title_connected_to); + mTitle.append(mConnectedDeviceName); + mConversationArrayAdapter.clear(); + break; + case BluetoothChatService.STATE_CONNECTING: + mTitle.setText(R.string.title_connecting); + break; + case BluetoothChatService.STATE_READY: + case BluetoothChatService.STATE_NONE: + mTitle.setText(R.string.title_not_connected); + break; + } + break; + case MESSAGE_WRITE: + byte[] writeBuf = (byte[]) msg.obj; + // construct a string from the buffer + String writeMessage = new String(writeBuf); + mConversationArrayAdapter.add("Me: " + writeMessage); + break; + case MESSAGE_READ: + byte[] readBuf = (byte[]) msg.obj; + // construct a string from the valid bytes in the buffer + String readMessage = new String(readBuf, 0, msg.arg1); + mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage); + break; + case MESSAGE_DEVICE_NAME: + // save the connected device's name + mConnectedDeviceName = msg.getData().getString(DEVICE_NAME); + Toast.makeText(getApplicationContext(), "Connected to " + + mConnectedDeviceName, Toast.LENGTH_SHORT).show(); + break; + case MESSAGE_TOAST: + Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST), + Toast.LENGTH_SHORT).show(); + break; + } + } + }; + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if(D) Log.d(TAG, "onActivityResult " + resultCode); + switch (requestCode) { + case REQUEST_CONNECT_DEVICE_SECURE: + // When DeviceListActivity returns with a device to connect + if (resultCode == Activity.RESULT_OK) { + connectDevice(data, true); + } + break; + case REQUEST_CONNECT_DEVICE_INSECURE: + // When DeviceListActivity returns with a device to connect + if (resultCode == Activity.RESULT_OK) { + connectDevice(data, false); + } + break; + case REQUEST_ENABLE_BT: + // When the request to enable Bluetooth returns + if (resultCode == Activity.RESULT_OK) { + // Bluetooth is now enabled, so set up a chat session + setupChat(); + } else { + // User did not enable Bluetooth or an error occured + Log.d(TAG, "BT not enabled"); + Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show(); + finish(); + } + } + } + + private void connectDevice(Intent data, boolean secure) { + // Get the device MAC address + String address = data.getExtras() + .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); + // Get the BLuetoothDevice object + BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); + // Attempt to connect to the device + mChatService.connect(device, secure); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.option_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + Intent serverIntent = null; + switch (item.getItemId()) { + case R.id.secure_connect_scan: + // Launch the DeviceListActivity to see devices and do scan + serverIntent = new Intent(this, DeviceListActivity.class); + startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE); + return true; + case R.id.insecure_connect_scan: + // Launch the DeviceListActivity to see devices and do scan + serverIntent = new Intent(this, DeviceListActivity.class); + startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE); + return true; + case R.id.discoverable: + // Ensure this device is discoverable by others + ensureDiscoverable(); + return true; + } + return false; + } + +} diff --git a/src/org/altusmetrum/AltosDroid/BluetoothChatService.java b/src/org/altusmetrum/AltosDroid/BluetoothChatService.java new file mode 100644 index 0000000..93cb75d --- /dev/null +++ b/src/org/altusmetrum/AltosDroid/BluetoothChatService.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 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. + */ + +package org.altusmetrum.AltosDroid; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +//import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +/** + * This class does all the work for setting up and managing Bluetooth + * connections with other devices. It has a thread that listens for + * incoming connections, a thread for connecting with a device, and a + * thread for performing data transmissions when connected. + */ +public class BluetoothChatService { + // Debugging + private static final String TAG = "BluetoothChatService"; + private static final boolean D = true; + + // Member fields + private final BluetoothAdapter mAdapter; + private final Handler mHandler; + private ConnectThread mConnectThread; + private ConnectedThread mConnectedThread; + private int mState; + + // Constants that indicate the current connection state + public static final int STATE_NONE = 0; // we're doing nothing + public static final int STATE_READY = 1; + public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection + public static final int STATE_CONNECTED = 3; // now connected to a remote device + + /** + * Constructor. Prepares a new BluetoothChat session. + * @param context The UI Activity Context + * @param handler A Handler to send messages back to the UI Activity + */ + public BluetoothChatService(Context context, Handler handler) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mState = STATE_NONE; + mHandler = handler; + } + + /** + * Set the current state of the chat connection + * @param state An integer defining the current connection state + */ + private synchronized void setState(int state) { + if (D) Log.d(TAG, "setState() " + mState + " -> " + state); + mState = state; + + // Give the new state to the Handler so the UI Activity can update + mHandler.obtainMessage(AltosDroid.MESSAGE_STATE_CHANGE, state, -1).sendToTarget(); + } + + /** + * Return the current connection state. */ + public synchronized int getState() { + return mState; + } + + /** + * Start the chat service. Specifically start AcceptThread to begin a + * session in listening (server) mode. Called by the Activity onResume() */ + public synchronized void start() { + if (D) Log.d(TAG, "start"); + + // Cancel any thread attempting to make a connection + if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} + + // Cancel any thread currently running a connection + if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} + + setState(STATE_READY); + + } + + /** + * Start the ConnectThread to initiate a connection to a remote device. + * @param device The BluetoothDevice to connect + * @param secure Socket Security type - Secure (true) , Insecure (false) + */ + public synchronized void connect(BluetoothDevice device, boolean secure) { + if (D) Log.d(TAG, "connect to: " + device); + + // Cancel any thread attempting to make a connection + if (mState == STATE_CONNECTING) { + if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} + } + + // Cancel any thread currently running a connection + if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} + + // Start the thread to connect with the given device + mConnectThread = new ConnectThread(device, secure); + mConnectThread.start(); + setState(STATE_CONNECTING); + } + + /** + * Start the ConnectedThread to begin managing a Bluetooth connection + * @param socket The BluetoothSocket on which the connection was made + * @param device The BluetoothDevice that has been connected + */ + public synchronized void connected(BluetoothSocket socket, BluetoothDevice + device, final String socketType) { + if (D) Log.d(TAG, "connected, Socket Type:" + socketType); + + // Cancel the thread that completed the connection + if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} + + // Cancel any thread currently running a connection + if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} + + // Start the thread to manage the connection and perform transmissions + mConnectedThread = new ConnectedThread(socket, socketType); + mConnectedThread.start(); + + // Send the name of the connected device back to the UI Activity + Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_DEVICE_NAME); + Bundle bundle = new Bundle(); + bundle.putString(AltosDroid.DEVICE_NAME, device.getName()); + msg.setData(bundle); + mHandler.sendMessage(msg); + + setState(STATE_CONNECTED); + } + + /** + * Stop all threads + */ + public synchronized void stop() { + if (D) Log.d(TAG, "stop"); + + if (mConnectThread != null) { + mConnectThread.cancel(); + mConnectThread = null; + } + + if (mConnectedThread != null) { + mConnectedThread.cancel(); + mConnectedThread = null; + } + + setState(STATE_NONE); + } + + /** + * Write to the ConnectedThread in an unsynchronized manner + * @param out The bytes to write + * @see ConnectedThread#write(byte[]) + */ + public void write(byte[] out) { + // Create temporary object + ConnectedThread r; + // Synchronize a copy of the ConnectedThread + synchronized (this) { + if (mState != STATE_CONNECTED) return; + r = mConnectedThread; + } + // Perform the write unsynchronized + r.write(out); + } + + /** + * Indicate that the connection attempt failed and notify the UI Activity. + */ + private void connectionFailed() { + // Send a failure message back to the Activity + Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_TOAST); + Bundle bundle = new Bundle(); + bundle.putString(AltosDroid.TOAST, "Unable to connect device"); + msg.setData(bundle); + mHandler.sendMessage(msg); + + // Start the service over to restart listening mode + BluetoothChatService.this.start(); + } + + /** + * Indicate that the connection was lost and notify the UI Activity. + */ + private void connectionLost() { + // Send a failure message back to the Activity + Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_TOAST); + Bundle bundle = new Bundle(); + bundle.putString(AltosDroid.TOAST, "Device connection was lost"); + msg.setData(bundle); + mHandler.sendMessage(msg); + + // Start the service over to restart listening mode + BluetoothChatService.this.start(); + } + + + /** + * This thread runs while attempting to make an outgoing connection + * with a device. It runs straight through; the connection either + * succeeds or fails. + */ + private class ConnectThread extends Thread { + private final BluetoothSocket mmSocket; + private final BluetoothDevice mmDevice; + private String mSocketType; + + public ConnectThread(BluetoothDevice device, boolean secure) { + mmDevice = device; + BluetoothSocket tmp = null; + mSocketType = secure ? "Secure" : "Insecure"; + + // Get a BluetoothSocket for a connection with the + // given BluetoothDevice + try { + if (secure) { + Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}); + tmp = (BluetoothSocket) m.invoke(device, 2); +// tmp = device.createRfcommSocket(1); + } else { + Method m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class}); + tmp = (BluetoothSocket) m.invoke(device, 2); +// tmp = device.createInsecureRfcommSocket(1); + } + } catch (Exception e) { + Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e); + e.printStackTrace(); + } + mmSocket = tmp; + } + + public void run() { + Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType); + setName("ConnectThread" + mSocketType); + + // Always cancel discovery because it will slow down a connection + mAdapter.cancelDiscovery(); + + // Make a connection to the BluetoothSocket + try { + // This is a blocking call and will only return on a + // successful connection or an exception + mmSocket.connect(); + } catch (IOException e) { + // Close the socket + try { + mmSocket.close(); + } catch (IOException e2) { + Log.e(TAG, "unable to close() " + mSocketType + + " socket during connection failure", e2); + } + connectionFailed(); + return; + } + + // Reset the ConnectThread because we're done + synchronized (BluetoothChatService.this) { + mConnectThread = null; + } + + // Start the connected thread + connected(mmSocket, mmDevice, mSocketType); + } + + public void cancel() { + try { + mmSocket.close(); + } catch (IOException e) { + Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e); + } + } + } + + /** + * This thread runs during a connection with a remote device. + * It handles all incoming and outgoing transmissions. + */ + private class ConnectedThread extends Thread { + private final BluetoothSocket mmSocket; + private final InputStream mmInStream; + private final OutputStream mmOutStream; + + public ConnectedThread(BluetoothSocket socket, String socketType) { + Log.d(TAG, "create ConnectedThread: " + socketType); + mmSocket = socket; + InputStream tmpIn = null; + OutputStream tmpOut = null; + + // Get the BluetoothSocket input and output streams + try { + tmpIn = socket.getInputStream(); + tmpOut = socket.getOutputStream(); + } catch (IOException e) { + Log.e(TAG, "temp sockets not created", e); + } + + mmInStream = tmpIn; + mmOutStream = tmpOut; + } + + public void run() { + Log.i(TAG, "BEGIN mConnectedThread"); + byte[] buffer = new byte[1024]; + int bytes; + + // Keep listening to the InputStream while connected + while (true) { + try { + // Read from the InputStream + bytes = mmInStream.read(buffer); + + // Send the obtained bytes to the UI Activity + mHandler.obtainMessage(AltosDroid.MESSAGE_READ, bytes, -1, buffer) + .sendToTarget(); + } catch (IOException e) { + Log.e(TAG, "disconnected", e); + connectionLost(); + break; + } + } + } + + /** + * Write to the connected OutStream. + * @param buffer The bytes to write + */ + public void write(byte[] buffer) { + try { + mmOutStream.write(buffer); + + // Share the sent message back to the UI Activity + mHandler.obtainMessage(AltosDroid.MESSAGE_WRITE, -1, -1, buffer) + .sendToTarget(); + } catch (IOException e) { + Log.e(TAG, "Exception during write", e); + } + } + + public void cancel() { + try { + mmSocket.close(); + } catch (IOException e) { + Log.e(TAG, "close() of connect socket failed", e); + } + } + } +} diff --git a/src/org/altusmetrum/AltosDroid/DeviceListActivity.java b/src/org/altusmetrum/AltosDroid/DeviceListActivity.java new file mode 100644 index 0000000..af5d7f1 --- /dev/null +++ b/src/org/altusmetrum/AltosDroid/DeviceListActivity.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 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. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.Set; +import org.altusmetrum.AltosDroid.R; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.View.OnClickListener; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.AdapterView.OnItemClickListener; + +/** + * This Activity appears as a dialog. It lists any paired devices and + * devices detected in the area after discovery. When a device is chosen + * by the user, the MAC address of the device is sent back to the parent + * Activity in the result Intent. + */ +public class DeviceListActivity extends Activity { + // Debugging + private static final String TAG = "DeviceListActivity"; + private static final boolean D = true; + + // Return Intent extra + public static String EXTRA_DEVICE_ADDRESS = "device_address"; + + // Member fields + private BluetoothAdapter mBtAdapter; + private ArrayAdapter<String> mPairedDevicesArrayAdapter; + private ArrayAdapter<String> mNewDevicesArrayAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.device_list); + + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + + // Initialize the button to perform device discovery + Button scanButton = (Button) findViewById(R.id.button_scan); + scanButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + doDiscovery(); + v.setVisibility(View.GONE); + } + }); + + // Initialize array adapters. One for already paired devices and + // one for newly discovered devices + mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name); + mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name); + + // Find and set up the ListView for paired devices + ListView pairedListView = (ListView) findViewById(R.id.paired_devices); + pairedListView.setAdapter(mPairedDevicesArrayAdapter); + pairedListView.setOnItemClickListener(mDeviceClickListener); + + // Find and set up the ListView for newly discovered devices + ListView newDevicesListView = (ListView) findViewById(R.id.new_devices); + newDevicesListView.setAdapter(mNewDevicesArrayAdapter); + newDevicesListView.setOnItemClickListener(mDeviceClickListener); + + // Register for broadcasts when a device is discovered + IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); + this.registerReceiver(mReceiver, filter); + + // Register for broadcasts when discovery has finished + filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); + this.registerReceiver(mReceiver, filter); + + // Get the local Bluetooth adapter + mBtAdapter = BluetoothAdapter.getDefaultAdapter(); + + // Get a set of currently paired devices + Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices(); + + // If there are paired devices, add each one to the ArrayAdapter + if (pairedDevices.size() > 0) { + findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE); + for (BluetoothDevice device : pairedDevices) { + mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); + } + } else { + String noDevices = getResources().getText(R.string.none_paired).toString(); + mPairedDevicesArrayAdapter.add(noDevices); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + // Make sure we're not doing discovery anymore + if (mBtAdapter != null) { + mBtAdapter.cancelDiscovery(); + } + + // Unregister broadcast listeners + this.unregisterReceiver(mReceiver); + } + + /** + * Start device discover with the BluetoothAdapter + */ + private void doDiscovery() { + if (D) Log.d(TAG, "doDiscovery()"); + + // Indicate scanning in the title + setProgressBarIndeterminateVisibility(true); + setTitle(R.string.scanning); + + // Turn on sub-title for new devices + findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE); + + // If we're already discovering, stop it + if (mBtAdapter.isDiscovering()) { + mBtAdapter.cancelDiscovery(); + } + + // Request discover from BluetoothAdapter + mBtAdapter.startDiscovery(); + } + + // The on-click listener for all devices in the ListViews + private OnItemClickListener mDeviceClickListener = new OnItemClickListener() { + public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) { + // Cancel discovery because it's costly and we're about to connect + mBtAdapter.cancelDiscovery(); + + // Get the device MAC address, which is the last 17 chars in the View + String info = ((TextView) v).getText().toString(); + String address = info.substring(info.length() - 17); + + // Create the result Intent and include the MAC address + Intent intent = new Intent(); + intent.putExtra(EXTRA_DEVICE_ADDRESS, address); + + // Set result and finish this Activity + setResult(Activity.RESULT_OK, intent); + finish(); + } + }; + + // The BroadcastReceiver that listens for discovered devices and + // changes the title when discovery is finished + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + // When discovery finds a device + if (BluetoothDevice.ACTION_FOUND.equals(action)) { + // Get the BluetoothDevice object from the Intent + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + // If it's already paired, skip it, because it's been listed already + if (device.getBondState() != BluetoothDevice.BOND_BONDED) { + mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); + } + // When discovery is finished, change the Activity title + } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { + setProgressBarIndeterminateVisibility(false); + setTitle(R.string.select_device); + if (mNewDevicesArrayAdapter.getCount() == 0) { + String noDevices = getResources().getText(R.string.none_found).toString(); + mNewDevicesArrayAdapter.add(noDevices); + } + } + } + }; + +} |