410 lines
15 KiB
Java
410 lines
15 KiB
Java
/*
|
|
* 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 org.surfsite.iconsole;
|
|
|
|
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.NumberPicker;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import org.surfsite.iconsole.R;
|
|
import org.surfsite.iconsole.common.logger.Log;
|
|
|
|
import java.util.Locale;
|
|
|
|
/**
|
|
* 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 Button mStartButton;
|
|
private Button mStopButton;
|
|
private Button mDisconnectButton;
|
|
private NumberPicker mLevel;
|
|
/**
|
|
* Name of the connected device
|
|
*/
|
|
private String mConnectedDeviceName = null;
|
|
|
|
/**
|
|
* Array adapter for the conversation thread
|
|
private ArrayAdapter<String> mConversationArrayAdapter;
|
|
*/
|
|
|
|
/**
|
|
* 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 (null == mBluetoothAdapter || !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);
|
|
mStartButton = (Button) view.findViewById(R.id.button_start);
|
|
mStopButton = (Button) view.findViewById(R.id.button_stop);
|
|
mDisconnectButton = (Button) view.findViewById(R.id.button_disconnect);
|
|
mLevel = (NumberPicker) view.findViewById(R.id.Level);
|
|
mLevel.setMaxValue(16);
|
|
mLevel.setMinValue(1);
|
|
mLevel.setValue(5);
|
|
mLevel.setWrapSelectorWheel(false);
|
|
mLevel.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
|
|
}
|
|
|
|
/**
|
|
* 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<>(getActivity(), R.layout.message);
|
|
|
|
mConversationView.setAdapter(mConversationArrayAdapter);
|
|
*/
|
|
|
|
// Initialize the BluetoothChatService to perform bluetooth connections
|
|
mChatService = new BluetoothChatService(getActivity(), mHandler);
|
|
|
|
mStartButton.setOnClickListener(new View.OnClickListener() {
|
|
public void onClick(View v) {
|
|
mChatService.startIConsole();
|
|
}
|
|
});
|
|
|
|
mStopButton.setOnClickListener(new View.OnClickListener() {
|
|
public void onClick(View v) {
|
|
mChatService.stopIConsole();
|
|
}
|
|
});
|
|
|
|
mDisconnectButton.setOnClickListener(new View.OnClickListener() {
|
|
public void onClick(View v) {
|
|
mChatService.stop();
|
|
}
|
|
});
|
|
|
|
mStartButton.setEnabled(false);
|
|
mStopButton.setEnabled(false);
|
|
mDisconnectButton.setEnabled(false);
|
|
}
|
|
|
|
/**
|
|
* Makes this device discoverable for 300 seconds (5 minutes).
|
|
*/
|
|
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;
|
|
}
|
|
|
|
mChatService.startIConsole();
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
FragmentActivity activity = getActivity();
|
|
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();
|
|
mStartButton.setEnabled(true);
|
|
mStopButton.setEnabled(true);
|
|
mDisconnectButton.setEnabled(true);
|
|
break;
|
|
case BluetoothChatService.STATE_CONNECTING:
|
|
setStatus(R.string.title_connecting);
|
|
mStartButton.setEnabled(false);
|
|
mStopButton.setEnabled(false);
|
|
mDisconnectButton.setEnabled(false);
|
|
break;
|
|
case BluetoothChatService.STATE_LISTEN:
|
|
case BluetoothChatService.STATE_NONE:
|
|
setStatus(R.string.title_not_connected);
|
|
mStartButton.setEnabled(false);
|
|
mStopButton.setEnabled(false);
|
|
mDisconnectButton.setEnabled(false);
|
|
break;
|
|
}
|
|
break;
|
|
case Constants.MESSAGE_DATA:
|
|
IConsole.Data data = (IConsole.Data) msg.obj;
|
|
// FIXME
|
|
// insert text here
|
|
/*
|
|
String dataMessage = String.format(Locale.US, "Time: %d Speed: %3.1f Power: %3.1f RPM: %d LVL: %d Dist: %4.1f Cal: %d HF: %d",
|
|
data.mTime, data.mSpeed10 / 10.0, data.mPower10 / 10.0, data.mRPM,
|
|
data.mLevel, data.mDistance10 / 10.0, data.mCalories, data.mHF);
|
|
*/
|
|
//mConversationArrayAdapter.add(mConnectedDeviceName + ": " + dataMessage);
|
|
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);
|
|
if (null != activity) {
|
|
Toast.makeText(activity, "Connected to "
|
|
+ mConnectedDeviceName, Toast.LENGTH_SHORT).show();
|
|
}
|
|
break;
|
|
case Constants.MESSAGE_TOAST:
|
|
if (null != activity) {
|
|
Toast.makeText(activity, 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 device
|
|
*
|
|
* @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);
|
|
}
|
|
|
|
@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;
|
|
}
|
|
|
|
}
|