diff options
author | Yuichi Araki <yaraki@google.com> | 2014-09-01 19:50:36 +0900 |
---|---|---|
committer | Yuichi Araki <yaraki@google.com> | 2014-09-02 16:53:24 +0900 |
commit | 0a5978937ff95a266c3a70415393c15fd340c1a3 (patch) | |
tree | 7f49b3acf2665c1e6333111d354dd7731f7d6437 | |
parent | 33cef3fa89217b4cd159c1eab2de4d2a136796f4 (diff) | |
download | android-0a5978937ff95a266c3a70415393c15fd340c1a3.tar.gz |
BluetoothChat: Porting from legacy directory.
Change-Id: I7d7b39f6cefdf7e3e40bdf1655df5a994af15f8a
30 files changed, 1873 insertions, 0 deletions
diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/.gitignore b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/.gitignore new file mode 100644 index 00000000..6eb878d4 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/.gitignore @@ -0,0 +1,16 @@ +# Copyright 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +src/template/ +src/common/ +build.gradle diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/README-fragmentview.txt b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/README-fragmentview.txt new file mode 100644 index 00000000..38d903f0 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/README-fragmentview.txt @@ -0,0 +1,37 @@ +<!-- + Copyright 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +Steps to implement FragmentView template: +-in template-params.xml.ftl: + -add the following line to common imports + <common src="activities"/> + +-Add a Fragment to show behavior. In your MainActivity.java class, it will reference a Fragment + called (yourProjectName)Fragment.java. Create that file in your project, using the "main" source + folder instead of "common" or "templates". + For instance, if your package name is com.example.foo, create the file + src/main/java/com/example/foo/FooFragment.java + + +-Within this fragment, make sure that the onCreate method has the line + "setHasOptionsMenu(true);", to enable the fragment to handle menu events. + +-In order to override menu events, override onOptionsItemSelected. + +-refer to sampleSamples/fragmentViewSample for a reference implementation of a +project built on this template. + + diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/proguard-project.txt b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/proguard-project.txt new file mode 100644 index 00000000..0d8f171d --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/proguard-project.txt @@ -0,0 +1,20 @@ + To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/AndroidManifest.xml b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/AndroidManifest.xml new file mode 100644 index 00000000..99b9b12e --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/AndroidManifest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest + package="com.example.android.bluetoothchat" + xmlns:android="http://schemas.android.com/apk/res/android" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk + android:minSdkVersion="11" + android:targetSdkVersion="17"/> + + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> + <uses-permission android:name="android.permission.BLUETOOTH"/> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme"> + + <activity + android:name=".MainActivity" + android:configChanges="orientation|keyboardHidden" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity + android:name=".DeviceListActivity" + android:configChanges="orientation|keyboardHidden" + android:label="@string/select_device" + android:theme="@android:style/Theme.Holo.Dialog"/> + + </application> + +</manifest> diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/BluetoothChatFragment.java b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/BluetoothChatFragment.java new file mode 100644 index 00000000..8ca2b533 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/BluetoothChatFragment.java @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.bluetoothchat; + +import android.app.ActionBar; +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.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +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 com.example.android.common.logger.Log; + +/** + * This fragment controls Bluetooth to communicate with other devices. + */ +public class BluetoothChatFragment extends Fragment { + + private static final String TAG = "BluetoothChatFragment"; + + // 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 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); + setHasOptionsMenu(true); + // Get local Bluetooth adapter + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + + // If the adapter is null, then Bluetooth is not supported + if (mBluetoothAdapter == null) { + FragmentActivity activity = getActivity(); + Toast.makeText(activity, "Bluetooth is not available", Toast.LENGTH_LONG).show(); + activity.finish(); + } + } + + + @Override + public void onStart() { + super.onStart(); + // 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 void onDestroy() { + super.onDestroy(); + if (mChatService != null) { + mChatService.stop(); + } + } + + @Override + public void onResume() { + super.onResume(); + + // 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(); + } + } + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_bluetooth_chat, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + mConversationView = (ListView) view.findViewById(R.id.in); + mOutEditText = (EditText) view.findViewById(R.id.edit_text_out); + mSendButton = (Button) view.findViewById(R.id.button_send); + } + + /** + * Set up the UI and background operations for chat. + */ + private void setupChat() { + Log.d(TAG, "setupChat()"); + + // Initialize the array adapter for the conversation thread + mConversationArrayAdapter = new ArrayAdapter<String>(getActivity(), R.layout.message); + + mConversationView.setAdapter(mConversationArrayAdapter); + + // Initialize the compose field with a listener for the return key + mOutEditText.setOnEditorActionListener(mWriteListener); + + // Initialize the send button with a listener that for click events + mSendButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + // Send a message using content of the edit text widget + View view = getView(); + if (null != view) { + TextView textView = (TextView) view.findViewById(R.id.edit_text_out); + String message = textView.getText().toString(); + sendMessage(message); + } + } + }); + + // Initialize the BluetoothChatService to perform bluetooth connections + mChatService = new BluetoothChatService(getActivity(), mHandler); + + // Initialize the buffer for outgoing messages + mOutStringBuffer = new StringBuffer(""); + } + + /** + * Makes this device discoverable. + */ + private void ensureDiscoverable() { + 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(getActivity(), 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); + } + return true; + } + }; + + /** + * Updates the status on the action bar. + * + * @param resId a string resource ID + */ + private void setStatus(int resId) { + FragmentActivity activity = getActivity(); + if (null == activity) { + return; + } + final ActionBar actionBar = activity.getActionBar(); + if (null == actionBar) { + return; + } + actionBar.setSubtitle(resId); + } + + /** + * Updates the status on the action bar. + * + * @param subTitle status + */ + private void setStatus(CharSequence subTitle) { + FragmentActivity activity = getActivity(); + if (null == activity) { + return; + } + final ActionBar actionBar = activity.getActionBar(); + if (null == actionBar) { + return; + } + actionBar.setSubtitle(subTitle); + } + + /** + * 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 Constants.MESSAGE_STATE_CHANGE: + switch (msg.arg1) { + case BluetoothChatService.STATE_CONNECTED: + setStatus(getString(R.string.title_connected_to, mConnectedDeviceName)); + mConversationArrayAdapter.clear(); + break; + case BluetoothChatService.STATE_CONNECTING: + setStatus(R.string.title_connecting); + break; + case BluetoothChatService.STATE_LISTEN: + case BluetoothChatService.STATE_NONE: + setStatus(R.string.title_not_connected); + break; + } + break; + case Constants.MESSAGE_WRITE: + byte[] writeBuf = (byte[]) msg.obj; + // construct a string from the buffer + String writeMessage = new String(writeBuf); + mConversationArrayAdapter.add("Me: " + writeMessage); + break; + case Constants.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 Constants.MESSAGE_DEVICE_NAME: + // save the connected device's name + mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME); + Toast.makeText(getActivity(), "Connected to " + + mConnectedDeviceName, Toast.LENGTH_SHORT).show(); + break; + case Constants.MESSAGE_TOAST: + Toast.makeText(getActivity(), msg.getData().getString(Constants.TOAST), + Toast.LENGTH_SHORT).show(); + break; + } + } + }; + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + 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 occurred + Log.d(TAG, "BT not enabled"); + Toast.makeText(getActivity(), R.string.bt_not_enabled_leaving, + Toast.LENGTH_SHORT).show(); + getActivity().finish(); + } + } + } + + /** + * Establish connection with other divice + * + * @param data An {@link Intent} with {@link DeviceListActivity#EXTRA_DEVICE_ADDRESS} extra. + * @param secure Socket Security type - Secure (true) , Insecure (false) + */ + 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 void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.bluetooth_chat, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.secure_connect_scan: { + // Launch the DeviceListActivity to see devices and do scan + Intent serverIntent = new Intent(getActivity(), 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 + Intent serverIntent = new Intent(getActivity(), 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/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/BluetoothChatService.java b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/BluetoothChatService.java new file mode 100644 index 00000000..b88b160d --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/BluetoothChatService.java @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.bluetoothchat; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothServerSocket; +import android.bluetooth.BluetoothSocket; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; + +import com.example.android.common.logger.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.UUID; + +/** + * 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"; + + // Name for the SDP record when creating server socket + private static final String NAME_SECURE = "BluetoothChatSecure"; + private static final String NAME_INSECURE = "BluetoothChatInsecure"; + + // Unique UUID for this application + private static final UUID MY_UUID_SECURE = + UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66"); + private static final UUID MY_UUID_INSECURE = + UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66"); + + // Member fields + private final BluetoothAdapter mAdapter; + private final Handler mHandler; + private AcceptThread mSecureAcceptThread; + private AcceptThread mInsecureAcceptThread; + 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_LISTEN = 1; // now listening for incoming connections + 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) { + Log.d(TAG, "setState() " + mState + " -> " + state); + mState = state; + + // Give the new state to the Handler so the UI Activity can update + mHandler.obtainMessage(Constants.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() { + 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_LISTEN); + + // Start the thread to listen on a BluetoothServerSocket + if (mSecureAcceptThread == null) { + mSecureAcceptThread = new AcceptThread(true); + mSecureAcceptThread.start(); + } + if (mInsecureAcceptThread == null) { + mInsecureAcceptThread = new AcceptThread(false); + mInsecureAcceptThread.start(); + } + } + + /** + * 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) { + 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) { + 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; + } + + // Cancel the accept thread because we only want to connect to one device + if (mSecureAcceptThread != null) { + mSecureAcceptThread.cancel(); + mSecureAcceptThread = null; + } + if (mInsecureAcceptThread != null) { + mInsecureAcceptThread.cancel(); + mInsecureAcceptThread = 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(Constants.MESSAGE_DEVICE_NAME); + Bundle bundle = new Bundle(); + bundle.putString(Constants.DEVICE_NAME, device.getName()); + msg.setData(bundle); + mHandler.sendMessage(msg); + + setState(STATE_CONNECTED); + } + + /** + * Stop all threads + */ + public synchronized void stop() { + Log.d(TAG, "stop"); + + if (mConnectThread != null) { + mConnectThread.cancel(); + mConnectThread = null; + } + + if (mConnectedThread != null) { + mConnectedThread.cancel(); + mConnectedThread = null; + } + + if (mSecureAcceptThread != null) { + mSecureAcceptThread.cancel(); + mSecureAcceptThread = null; + } + + if (mInsecureAcceptThread != null) { + mInsecureAcceptThread.cancel(); + mInsecureAcceptThread = 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(Constants.MESSAGE_TOAST); + Bundle bundle = new Bundle(); + bundle.putString(Constants.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(Constants.MESSAGE_TOAST); + Bundle bundle = new Bundle(); + bundle.putString(Constants.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 listening for incoming connections. It behaves + * like a server-side client. It runs until a connection is accepted + * (or until cancelled). + */ + private class AcceptThread extends Thread { + // The local server socket + private final BluetoothServerSocket mmServerSocket; + private String mSocketType; + + public AcceptThread(boolean secure) { + BluetoothServerSocket tmp = null; + mSocketType = secure ? "Secure" : "Insecure"; + + // Create a new listening server socket + try { + if (secure) { + tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, + MY_UUID_SECURE); + } else { + tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord( + NAME_INSECURE, MY_UUID_INSECURE); + } + } catch (IOException e) { + Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e); + } + mmServerSocket = tmp; + } + + public void run() { + Log.d(TAG, "Socket Type: " + mSocketType + + "BEGIN mAcceptThread" + this); + setName("AcceptThread" + mSocketType); + + BluetoothSocket socket = null; + + // Listen to the server socket if we're not connected + while (mState != STATE_CONNECTED) { + try { + // This is a blocking call and will only return on a + // successful connection or an exception + socket = mmServerSocket.accept(); + } catch (IOException e) { + Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e); + break; + } + + // If a connection was accepted + if (socket != null) { + synchronized (BluetoothChatService.this) { + switch (mState) { + case STATE_LISTEN: + case STATE_CONNECTING: + // Situation normal. Start the connected thread. + connected(socket, socket.getRemoteDevice(), + mSocketType); + break; + case STATE_NONE: + case STATE_CONNECTED: + // Either not ready or already connected. Terminate new socket. + try { + socket.close(); + } catch (IOException e) { + Log.e(TAG, "Could not close unwanted socket", e); + } + break; + } + } + } + } + Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType); + + } + + public void cancel() { + Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this); + try { + mmServerSocket.close(); + } catch (IOException e) { + Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e); + } + } + } + + + /** + * 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) { + tmp = device.createRfcommSocketToServiceRecord( + MY_UUID_SECURE); + } else { + tmp = device.createInsecureRfcommSocketToServiceRecord( + MY_UUID_INSECURE); + } + } catch (IOException e) { + Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e); + } + 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(Constants.MESSAGE_READ, bytes, -1, buffer) + .sendToTarget(); + } catch (IOException e) { + Log.e(TAG, "disconnected", e); + connectionLost(); + // Start the service over to restart listening mode + BluetoothChatService.this.start(); + 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(Constants.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/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/Constants.java b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/Constants.java new file mode 100644 index 00000000..3500e8e7 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/Constants.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.bluetoothchat; + +/** + * Defines several constants used between {@link BluetoothChatService} and the UI. + */ +public interface Constants { + + // 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"; + +} diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/DeviceListActivity.java b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/DeviceListActivity.java new file mode 100644 index 00000000..8b70adc4 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/java/com/example/android/bluetoothchat/DeviceListActivity.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.bluetoothchat; + +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.view.View; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; + +import com.example.android.common.logger.Log; + +import java.util.Set; + +/** + * 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 { + + /** + * Tag for Log + */ + private static final String TAG = "DeviceListActivity"; + + /** + * Return Intent extra + */ + public static String EXTRA_DEVICE_ADDRESS = "device_address"; + + /** + * Member fields + */ + private BluetoothAdapter mBtAdapter; + + /** + * Newly discovered devices + */ + private ArrayAdapter<String> mNewDevicesArrayAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.activity_device_list); + + // Set result CANCELED in case 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 View.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 + ArrayAdapter<String> pairedDevicesArrayAdapter = + 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(pairedDevicesArrayAdapter); + 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) { + pairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); + } + } else { + String noDevices = getResources().getText(R.string.none_paired).toString(); + pairedDevicesArrayAdapter.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() { + 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 AdapterView.OnItemClickListener mDeviceClickListener + = new AdapterView.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); + } + } + } + }; + +} diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-hdpi/ic_action_device_access_bluetooth_searching.png b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-hdpi/ic_action_device_access_bluetooth_searching.png Binary files differnew file mode 100755 index 00000000..fc0491e6 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-hdpi/ic_action_device_access_bluetooth_searching.png diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-hdpi/ic_launcher.png b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..092887b7 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-mdpi/ic_action_device_access_bluetooth_searching.png b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-mdpi/ic_action_device_access_bluetooth_searching.png Binary files differnew file mode 100755 index 00000000..d65de025 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-mdpi/ic_action_device_access_bluetooth_searching.png diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-mdpi/ic_launcher.png b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..1c51ccd7 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xhdpi/ic_action_device_access_bluetooth_searching.png b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xhdpi/ic_action_device_access_bluetooth_searching.png Binary files differnew file mode 100755 index 00000000..c4b236ee --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xhdpi/ic_action_device_access_bluetooth_searching.png diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xhdpi/ic_launcher.png b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..3fa260b7 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xxhdpi/ic_action_device_access_bluetooth_searching.png b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xxhdpi/ic_action_device_access_bluetooth_searching.png Binary files differnew file mode 100755 index 00000000..de264301 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xxhdpi/ic_action_device_access_bluetooth_searching.png diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xxhdpi/ic_launcher.png b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..6beccf39 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/activity_device_list.xml b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/activity_device_list.xml new file mode 100644 index 00000000..ae7242cf --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/activity_device_list.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + > + + <TextView + android:id="@+id/title_paired_devices" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="#666" + android:paddingLeft="5dp" + android:text="@string/title_paired_devices" + android:textColor="#fff" + android:visibility="gone" + /> + + <ListView + android:id="@+id/paired_devices" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:stackFromBottom="true" + /> + + <TextView + android:id="@+id/title_new_devices" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="#666" + android:paddingLeft="5dp" + android:text="@string/title_other_devices" + android:textColor="#fff" + android:visibility="gone" + /> + + <ListView + android:id="@+id/new_devices" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="2" + android:stackFromBottom="true" + /> + + <Button + android:id="@+id/button_scan" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/button_scan" + /> +</LinearLayout> diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/device_name.xml b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/device_name.xml new file mode 100644 index 00000000..28f57cca --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/device_name.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="5dp" + android:textSize="18sp" + /> diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/fragment_bluetooth_chat.xml b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/fragment_bluetooth_chat.xml new file mode 100644 index 00000000..91bad206 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/fragment_bluetooth_chat.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<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/in" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:stackFromBottom="true" + android:transcriptMode="alwaysScroll" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <EditText + android:id="@+id/edit_text_out" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_weight="1" /> + + <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/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/message.xml b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/message.xml new file mode 100644 index 00000000..28f57cca --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/layout/message.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="5dp" + android:textSize="18sp" + /> diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/menu/bluetooth_chat.xml b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/menu/bluetooth_chat.xml new file mode 100644 index 00000000..a965104b --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/menu/bluetooth_chat.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@+id/secure_connect_scan" + android:icon="@drawable/ic_action_device_access_bluetooth_searching" + android:showAsAction="ifRoom" + android:title="@string/secure_connect"/> + + <item + android:id="@+id/insecure_connect_scan" + android:showAsAction="never" + android:title="@string/insecure_connect"/> + + <item + android:id="@+id/discoverable" + android:showAsAction="never" + android:title="@string/discoverable"/> + +</menu> diff --git a/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/values/strings.xml b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/values/strings.xml new file mode 100644 index 00000000..a38d82da --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/BluetoothChatSample/src/main/res/values/strings.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- 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 to <xliff:g id="device_name">%1$s</xliff:g></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/connectivity/bluetooth/BluetoothChat/build.gradle b/connectivity/bluetooth/BluetoothChat/build.gradle new file mode 100644 index 00000000..be1fa823 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/build.gradle @@ -0,0 +1,14 @@ + + + + +// BEGIN_EXCLUDE +import com.example.android.samples.build.SampleGenPlugin +apply plugin: SampleGenPlugin + +samplegen { + pathToBuild "../../../../../build" + pathToSamplesCommon "../../../common" +} +apply from: "../../../../../build/build.gradle" +// END_EXCLUDE diff --git a/connectivity/bluetooth/BluetoothChat/buildSrc/build.gradle b/connectivity/bluetooth/BluetoothChat/buildSrc/build.gradle new file mode 100644 index 00000000..e344a8cb --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/buildSrc/build.gradle @@ -0,0 +1,18 @@ + + + +repositories { + mavenCentral() +} +dependencies { + compile 'org.freemarker:freemarker:2.3.20' +} + +sourceSets { + main { + groovy { + srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy") + } + } +} + diff --git a/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.jar b/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..8c0fb64a --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.jar diff --git a/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties b/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..d7f03cfe --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip diff --git a/connectivity/bluetooth/BluetoothChat/gradlew b/connectivity/bluetooth/BluetoothChat/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/connectivity/bluetooth/BluetoothChat/gradlew.bat b/connectivity/bluetooth/BluetoothChat/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/connectivity/bluetooth/BluetoothChat/settings.gradle b/connectivity/bluetooth/BluetoothChat/settings.gradle new file mode 100644 index 00000000..9410c659 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/settings.gradle @@ -0,0 +1,4 @@ + + + +include 'BluetoothChatSample' diff --git a/connectivity/bluetooth/BluetoothChat/template-params.xml b/connectivity/bluetooth/BluetoothChat/template-params.xml new file mode 100644 index 00000000..a4ca4978 --- /dev/null +++ b/connectivity/bluetooth/BluetoothChat/template-params.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<sample> + <name>BluetoothChat</name> + <group>Connectivity</group> + <package>com.example.android.bluetoothchat</package> + + <!-- change minSdk if needed--> + <minSdk>11</minSdk> + + <!-- Include additional dependencies here.--> + <!-- dependency>com.google.android.gms:play-services:5.0.+</dependency --> + + <strings> + <intro> + <![CDATA[ + This application allows two Android devices to carry out two-way text chat over + Bluetooth. It demonstrates all the fundamental Bluetooth API capabilites, such as: + (1) Scanning for other Bluetooth devices + (2) Querying the local Bluetooth adapter for paired Bluetooth devices + (3) Establishing RFCOMM channels/sockets + (4) Connecting to a remote device + (5) Transfering data over Bluetooth + ]]> + </intro> + </strings> + + <!-- The basic templates have already been enabled. Uncomment more as desired. --> + <template src="base" /> + <template src="FragmentView" /> + + <!-- Include common code modules by uncommenting them below. --> + <common src="logger" /> + <common src="activities"/> +</sample> |