Java tutorial
/* * Copyright (C) 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. */ package org.portlandrobotics.nrfUARTv2; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.text.DateFormat; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.Queue; import org.portlandrobotics.nrfUARTv2.UartService; import android.app.Activity; import android.app.AlertDialog; import android.app.Notification; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.res.Configuration; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RadioGroup; import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity implements RadioGroup.OnCheckedChangeListener { public static final int REQUEST_SELECT_DEVICE = 1; public static final int REQUEST_ENABLE_BT = 2; public static final int UART_PROFILE_READY = 10; public static final String TAG = "nRFUART"; public static final int UART_PROFILE_CONNECTED = 20; public static final int UART_PROFILE_DISCONNECTED = 21; public static final int STATE_OFF = 10; public static final int BTSERVO_REG_TARGET = 0; public static final int BTSERVO_REG_MODE = 1; public static final int BTSERVO_REG_SPEED = 2; public static final int BTSERVO_REG_LOCATION = 6; public static final int BTSERVO_REG_PWM = 7; TextView mRemoteRssiVal; RadioGroup mRg; private int mState = UART_PROFILE_DISCONNECTED; private UartService mService = null; private BluetoothDevice mDevice = null; private BluetoothAdapter mBtAdapter = null; private ListView messageListView; private ArrayAdapter<String> listAdapter; private Button btnConnectDisconnect, btnSend, btnRotateNinety; private EditText edtMessage; private ServoWheelView mSwv; private Switch motorSwitch; private final Charset uartCharset; private Handler mHandler = new Handler(); class ServoState { int target = 0; int mode = 0; int speed = 0; int location = 0; int pwm = 0; } ServoState mServoState = new ServoState(); class Command { public String command; public boolean sent = false; public Date timeout; } private Queue<Command> mCommandQueue = new LinkedList<Command>(); //int rotation = 0; public MainActivity() { this.uartCharset = Charset.forName("US-ASCII"); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mBtAdapter = BluetoothAdapter.getDefaultAdapter(); if (mBtAdapter == null) { Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); finish(); return; } messageListView = (ListView) findViewById(R.id.listMessage); listAdapter = new ArrayAdapter<String>(this, R.layout.message_detail); messageListView.setAdapter(listAdapter); messageListView.setDivider(null); btnConnectDisconnect = (Button) findViewById(R.id.btn_select); btnSend = (Button) findViewById(R.id.sendButton); btnRotateNinety = (Button) findViewById(R.id.rotateNinetyButton); edtMessage = (EditText) findViewById(R.id.sendText); mSwv = (ServoWheelView) findViewById(R.id.view); motorSwitch = (Switch) findViewById(R.id.switch1); service_init(); mSwv.setAngleUpdate(new MyAngle()); // Handler Disconnect & Connect button btnConnectDisconnect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mBtAdapter.isEnabled()) { Log.i(TAG, "onClick - BT not enabled yet"); Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, REQUEST_ENABLE_BT); } else { if (btnConnectDisconnect.getText().equals("Connect")) { //Connect button pressed, open DeviceListActivity class, with popup windows that scan for devices Intent newIntent = new Intent(MainActivity.this, DeviceListActivity.class); startActivityForResult(newIntent, REQUEST_SELECT_DEVICE); } else { //Disconnect button pressed if (mDevice != null) { mService.disconnect(); } } } } }); // Handler Send button btnSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EditText editText = (EditText) findViewById(R.id.sendText); String message = editText.getText().toString(); if (message.equals("dump")) { readRegister(BTSERVO_REG_TARGET); readRegister(BTSERVO_REG_MODE); readRegister(BTSERVO_REG_SPEED); readRegister(BTSERVO_REG_LOCATION); readRegister(BTSERVO_REG_PWM); } else { //send data to service //byte[] value = message.getBytes(uartCharset); //mService.writeRXCharacteristic(value); queueCommand(message); } //Update the log with time stamp String currentDateTimeString = DateFormat.getTimeInstance().format(new Date()); listAdapter.add("[" + currentDateTimeString + "] TX: " + message); messageListView.smoothScrollToPosition(listAdapter.getCount() - 1); edtMessage.setText(""); } }); btnRotateNinety.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { mServoState.target = (mServoState.target + 45) % 1024; writeRegister(0, mServoState.target); } }); motorSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) writeRegister(1, 1); else writeRegister(1, 0); } }); // Set initial UI state } public void queueCommand(String command) { Command cmd = new Command(); cmd.command = command; mCommandQueue.add(cmd); processCommandQueue(); } public void log(String msg) { //Update the log with time stamp String currentDateTimeString = DateFormat.getTimeInstance().format(new Date()); listAdapter.add("[" + currentDateTimeString + "] LOG: " + msg); messageListView.smoothScrollToPosition(listAdapter.getCount() - 1); } public void readRegister(int reg) { queueCommand(String.format("%02x%02x", 0x00, reg)); } public void writeRegister(int reg, int value) { String c = String.format("%02x%02x%08x", 0x80, reg, value); queueCommand(c); } public void commandResponse(byte[] data) { String text = new String(data, uartCharset); Command c = mCommandQueue.peek(); if (c == null || !c.sent) { log("out of band " + text); } else { mCommandQueue.remove(); long dt = new Date().getTime() - c.timeout.getTime() + 1000; log(c.command + " -> " + text + " (" + dt + ")"); int ci = Integer.parseInt(c.command.substring(0, 4), 16); int cmdBack = Integer.parseInt(text.substring(0, 4), 16); int dataBack = Integer.parseInt(text.substring(4), 16); if (ci != cmdBack) log(String.format("debug %x %x %d", ci, cmdBack, dataBack)); if (cmdBack == BTSERVO_REG_LOCATION) { Log.v("Draw", String.format("%d %f", dataBack, dataBack / 1024. * 2 * Math.PI)); mSwv.setAngle(dataBack / 1024. * 2 * Math.PI); } } processCommandQueue(); } public void processCommandQueue() { Iterator<Command> e = mCommandQueue.iterator(); int i = 0; while (e.hasNext()) { // max 5 commands in flight if (++i > 1) break; Command c = e.next(); if (!c.sent) { if (!c.command.equals("0006")) log("TX: " + c.command); mService.writeRXCharacteristic(c.command.getBytes(uartCharset)); c.sent = true; c.timeout = new Date(new Date().getTime() + 1000); } } } //UART service connected/disconnected private ServiceConnection mServiceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder rawBinder) { mService = ((UartService.LocalBinder) rawBinder).getService(); Log.d(TAG, "onServiceConnected mService= " + mService); if (!mService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } } public void onServiceDisconnected(ComponentName classname) { //// mService.disconnect(mDevice); mService = null; } }; /*private Handler mHandler = new Handler() { @Override //Handler events that received from UART service public void handleMessage(Message msg) { } };*/ class MyAngle implements ServoWheelView.AngleUpdate { @Override public void OnAngleUpdate(double angle) { if (angle < 0) angle += 2 * Math.PI; int i = (int) (angle / 2 / Math.PI * 1024); mServoState.target = i; } } class DumpRunner implements Runnable { int cacheTarget; public void run() { Log.v("RUN", "Run in"); if (mState != UART_PROFILE_CONNECTED) return; if (mServoState.target != cacheTarget) { writeRegister(BTSERVO_REG_TARGET, mServoState.target); cacheTarget = mServoState.target; } readRegister(BTSERVO_REG_LOCATION); //readRegister(BTSERVO_REG_PWM); mHandler.postDelayed(this, 200); Log.v("RUN", "Run out"); } } private final BroadcastReceiver UARTStatusChangeReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); //final Intent mIntent = intent; //*********************// if (action.equals(UartService.ACTION_GATT_CONNECTED)) { runOnUiThread(new Runnable() { public void run() { String currentDateTimeString = DateFormat.getTimeInstance().format(new Date()); Log.d(TAG, "UART_CONNECT_MSG"); btnConnectDisconnect.setText("Disconnect"); edtMessage.setEnabled(true); btnSend.setEnabled(true); mHandler.postDelayed(new DumpRunner(), 500); btnRotateNinety.setEnabled(true); ((TextView) findViewById(R.id.deviceName)).setText(mDevice.getName() + " - ready"); listAdapter.add("[" + currentDateTimeString + "] Connected to: " + mDevice.getName()); messageListView.smoothScrollToPosition(listAdapter.getCount() - 1); mState = UART_PROFILE_CONNECTED; } }); } //*********************// if (action.equals(UartService.ACTION_GATT_DISCONNECTED)) { runOnUiThread(new Runnable() { public void run() { String currentDateTimeString = DateFormat.getTimeInstance().format(new Date()); Log.d(TAG, "UART_DISCONNECT_MSG"); btnConnectDisconnect.setText("Connect"); edtMessage.setEnabled(false); btnSend.setEnabled(false); ((TextView) findViewById(R.id.deviceName)).setText("Not Connected"); listAdapter.add("[" + currentDateTimeString + "] Disconnected to: " + mDevice.getName()); mState = UART_PROFILE_DISCONNECTED; mService.close(); //setUiState(); } }); } //*********************// if (action.equals(UartService.ACTION_GATT_SERVICES_DISCOVERED)) { mService.enableTXNotification(); } //*********************// if (action.equals(UartService.ACTION_DATA_AVAILABLE)) { final byte[] txValue = intent.getByteArrayExtra(UartService.EXTRA_DATA); runOnUiThread(new Runnable() { public void run() { commandResponse(txValue); /* try { String text = new String(txValue, "UTF-8"); String currentDateTimeString = DateFormat.getTimeInstance().format(new Date()); listAdapter.add("["+currentDateTimeString+"] RX: "+text); messageListView.smoothScrollToPosition(listAdapter.getCount() - 1); } catch (Exception e) { Log.e(TAG, e.toString()); }*/ } }); } //*********************// if (action.equals(UartService.DEVICE_DOES_NOT_SUPPORT_UART)) { showMessage("Device doesn't support UART. Disconnecting"); mService.disconnect(); } } }; private void service_init() { Intent bindIntent = new Intent(this, UartService.class); bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE); LocalBroadcastManager.getInstance(this).registerReceiver(UARTStatusChangeReceiver, makeGattUpdateIntentFilter()); } private static IntentFilter makeGattUpdateIntentFilter() { final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(UartService.ACTION_GATT_CONNECTED); intentFilter.addAction(UartService.ACTION_GATT_DISCONNECTED); intentFilter.addAction(UartService.ACTION_GATT_SERVICES_DISCOVERED); intentFilter.addAction(UartService.ACTION_DATA_AVAILABLE); intentFilter.addAction(UartService.DEVICE_DOES_NOT_SUPPORT_UART); return intentFilter; } @Override public void onStart() { super.onStart(); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy()"); try { LocalBroadcastManager.getInstance(this).unregisterReceiver(UARTStatusChangeReceiver); } catch (Exception ignore) { Log.e(TAG, ignore.toString()); } unbindService(mServiceConnection); mService.stopSelf(); mService = null; } @Override protected void onStop() { Log.d(TAG, "onStop"); super.onStop(); } @Override protected void onPause() { Log.d(TAG, "onPause"); super.onPause(); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "onRestart"); } @Override public void onResume() { super.onResume(); Log.d(TAG, "onResume"); if (!mBtAdapter.isEnabled()) { Log.i(TAG, "onResume - BT not enabled yet"); Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, REQUEST_ENABLE_BT); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_SELECT_DEVICE: //When the DeviceListActivity return, with the selected device address if (resultCode == Activity.RESULT_OK && data != null) { String deviceAddress = data.getStringExtra(BluetoothDevice.EXTRA_DEVICE); mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(deviceAddress); Log.d(TAG, "... onActivityResultdevice.address==" + mDevice + "mserviceValue" + mService); ((TextView) findViewById(R.id.deviceName)).setText(mDevice.getName() + " - connecting"); mService.connect(deviceAddress); } break; case REQUEST_ENABLE_BT: // When the request to enable Bluetooth returns if (resultCode == Activity.RESULT_OK) { Toast.makeText(this, "Bluetooth has turned on ", Toast.LENGTH_SHORT).show(); } else { // User did not enable Bluetooth or an error occurred Log.d(TAG, "BT not enabled"); Toast.makeText(this, "Problem in BT Turning ON ", Toast.LENGTH_SHORT).show(); finish(); } break; default: Log.e(TAG, "wrong request code"); break; } } @Override public void onCheckedChanged(RadioGroup group, int checkedId) { } private void showMessage(String msg) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); } @Override public void onBackPressed() { if (mState == UART_PROFILE_CONNECTED) { Intent startMain = new Intent(Intent.ACTION_MAIN); startMain.addCategory(Intent.CATEGORY_HOME); startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(startMain); showMessage("nRFUART's running in background.\n Disconnect to exit"); } else { new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.popup_title) .setMessage(R.string.popup_message) .setPositiveButton(R.string.popup_yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }).setNegativeButton(R.string.popup_no, null).show(); } } }