summaryrefslogtreecommitdiff
path: root/app/src/main/java/no/topi/fiken/display
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/no/topi/fiken/display')
-rw-r--r--app/src/main/java/no/topi/fiken/display/Constants.java10
-rw-r--r--app/src/main/java/no/topi/fiken/display/DefaultDisplayService.java255
-rw-r--r--app/src/main/java/no/topi/fiken/display/DisplayControlActivity.java141
-rw-r--r--app/src/main/java/no/topi/fiken/display/DisplayService.java102
-rw-r--r--app/src/main/java/no/topi/fiken/display/ExceptionHandler.java22
-rw-r--r--app/src/main/java/no/topi/fiken/display/MainActivity.java335
6 files changed, 865 insertions, 0 deletions
diff --git a/app/src/main/java/no/topi/fiken/display/Constants.java b/app/src/main/java/no/topi/fiken/display/Constants.java
new file mode 100644
index 0000000..f568e1c
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/Constants.java
@@ -0,0 +1,10 @@
+package no.topi.fiken.display;
+
+import java.util.UUID;
+
+public interface Constants {
+ String TRYGVIS_IO_BASE_UUID = "32D0xxxx-035D-59C5-70D3-BC8E4A1FD83F";
+ UUID TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0001"));
+ UUID TRYGVIS_IO_GAUGE_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0002"));
+ UUID TRYGVIS_IO_LED_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0003"));
+}
diff --git a/app/src/main/java/no/topi/fiken/display/DefaultDisplayService.java b/app/src/main/java/no/topi/fiken/display/DefaultDisplayService.java
new file mode 100644
index 0000000..b1aa2e7
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/DefaultDisplayService.java
@@ -0,0 +1,255 @@
+package no.topi.fiken.display;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.Toast;
+
+public class DefaultDisplayService extends Service implements DisplayService {
+ private final Context context = DefaultDisplayService.this;
+ private final static String TAG = DefaultDisplayService.class.getSimpleName();
+
+ private final IBinder binder = new LocalBinder(this);
+
+ private BluetoothManager mBluetoothManager;
+ private BluetoothAdapter mBluetoothAdapter;
+ private BluetoothGattService displayService;
+ private BluetoothGatt gatt;
+
+ private Handler handler;
+ private int UPDATE_RSSI_DELAY = 1000;
+
+ private Runnable updateRssi = new Runnable() {
+ @Override
+ public void run() {
+ if(gatt != null) {
+ gatt.readRemoteRssi();
+ }
+
+ handler.postDelayed(this, UPDATE_RSSI_DELAY);
+ }
+ };
+
+ public static enum ServiceState {
+ BROKEN,
+ IDLE,
+ SCANNING,
+ CONNECTED,
+ }
+
+ private ServiceState serviceState = ServiceState.BROKEN;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ @Override
+ public void onCreate() {
+ handler = new Handler();
+
+ handler.postDelayed(updateRssi, UPDATE_RSSI_DELAY);
+ }
+
+ public boolean initialize() {
+ // For API level 18 and above, get a reference to BluetoothAdapter through
+ // BluetoothManager.
+ if (mBluetoothManager == null) {
+ mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ if (mBluetoothManager == null) {
+ Log.e(TAG, "Unable to initialize BluetoothManager.");
+ return false;
+ }
+ }
+
+ mBluetoothAdapter = mBluetoothManager.getAdapter();
+ if (mBluetoothAdapter == null) {
+ Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
+ return false;
+ }
+
+ Log.e(TAG, "Bluetooth initialized");
+
+ serviceState = ServiceState.IDLE;
+
+ return true;
+ }
+
+ @Override
+ public boolean connect(final String address) {
+ if (serviceState != ServiceState.IDLE) {
+ if (!(serviceState == ServiceState.CONNECTED && gatt.getDevice().getAddress().equals(address))) {
+ Log.e(TAG, "connect(): Not idle: " + serviceState);
+ return false;
+ }
+
+ Log.i(TAG, "connect(): already connected: " + serviceState);
+ return true;
+ }
+
+ BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+
+ gatt = device.connectGatt(this, false, new BluetoothGattCallback() {
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+// Toast.makeText(context, "Connected", Toast.LENGTH_SHORT).show();
+ if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) {
+ boolean ok = gatt.discoverServices();
+
+ if (!ok) {
+ disconnect();
+ } else {
+ Intent intent = IntentAction.DEVICE_UPDATE.intent();
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), address);
+ intent.putExtra(IntentExtra.CONNECTED.name(), true);
+ sendBroadcast(intent);
+ }
+ } else {
+ Log.w(TAG, "Could not connect to device");
+// Toast.makeText(context, "Could not connect to device", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ Log.i(TAG, "onServicesDiscovered");
+
+ Log.i(TAG, "Constants.TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID = " + Constants.TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID);
+
+ for (BluetoothGattService bluetoothGattService : gatt.getServices()) {
+ Log.i(TAG, "bluetoothGattService.getUuid() = " + bluetoothGattService.getUuid());
+ }
+
+ displayService = gatt.getService(Constants.TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID);
+
+ Log.i(TAG, "service=" + displayService);
+
+ Intent intent = IntentAction.DEVICE_UPDATE.intent();
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), address);
+ intent.putExtra(IntentExtra.DEVICE_IS_DISPLAY.name(), displayService != null);
+ sendBroadcast(intent);
+ }
+
+ @Override
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ Log.i(TAG, "onCharacteristicRead");
+ }
+
+ @Override
+ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ Log.i(TAG, "onCharacteristicWrite");
+ }
+
+ @Override
+ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ Log.i(TAG, "onCharacteristicChanged");
+ }
+
+ @Override
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ Log.i(TAG, "onDescriptorRead");
+ }
+
+ @Override
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ Log.i(TAG, "onDescriptorWrite");
+ }
+
+ @Override
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+ Log.i(TAG, "onReliableWriteCompleted");
+ }
+
+ @Override
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+ Log.i(TAG, "onReadRemoteRssi, status=" + status + ", rssi=" + rssi);
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ Intent intent = IntentAction.DEVICE_UPDATE.intent();
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), gatt.getDevice().getAddress());
+ intent.putExtra(IntentExtra.RSSI.name(), rssi);
+ sendBroadcast(intent);
+ }
+ }
+ });
+
+ if (gatt != null) {
+ serviceState = ServiceState.CONNECTED;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ if (serviceState != ServiceState.CONNECTED) {
+ Log.d(TAG, "disconnect(): Not connected: " + serviceState);
+ return;
+ }
+
+ serviceState = ServiceState.IDLE;
+
+ if (gatt != null) {
+ try {
+ gatt.disconnect();
+ } catch (Exception e) {
+ Log.w(TAG, "gatt.disconnect()", e);
+ }
+ try {
+ gatt.close();
+ } catch (Exception e) {
+ Log.w(TAG, "gatt.close()", e);
+ }
+ gatt = null;
+ }
+
+ displayService = null;
+ }
+
+ @Override
+ public void startScan() {
+ if (serviceState != ServiceState.IDLE) {
+ Toast.makeText(context, "startScan(): Not idle", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ serviceState = ServiceState.SCANNING;
+
+ mBluetoothAdapter.startLeScan(leScanCallback);
+ }
+
+ @Override
+ public void stopScan() {
+ Log.d(TAG, "stopScan(): stopping scanning");
+
+ if (serviceState != ServiceState.SCANNING) {
+ Log.d(TAG, "stopScan(): not scanning");
+ return;
+ }
+ mBluetoothAdapter.stopLeScan(leScanCallback);
+ serviceState = ServiceState.IDLE;
+ }
+
+ private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
+ @Override
+ public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
+ Log.i(TAG, "onLeScan()");
+ Intent intent = IntentAction.DEVICE_UPDATE.intent();
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), device.getAddress());
+ intent.putExtra(IntentExtra.DEVICE_NAME.name(), device.getName());
+ intent.putExtra(IntentExtra.RSSI.name(), rssi);
+ sendBroadcast(intent);
+ }
+ };
+}
diff --git a/app/src/main/java/no/topi/fiken/display/DisplayControlActivity.java b/app/src/main/java/no/topi/fiken/display/DisplayControlActivity.java
new file mode 100644
index 0000000..1147c0c
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/DisplayControlActivity.java
@@ -0,0 +1,141 @@
+package no.topi.fiken.display;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.Menu;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import static java.lang.String.valueOf;
+import static no.topi.fiken.display.DisplayService.DeviceInfo;
+import static no.topi.fiken.display.DisplayService.IntentAction;
+import static no.topi.fiken.display.DisplayService.IntentExtra;
+import static no.topi.fiken.display.DisplayService.LocalBinder;
+
+public class DisplayControlActivity extends Activity {
+ private final static String TAG = DisplayControlActivity.class.getSimpleName();
+
+ private DisplayService displayService;
+ private DeviceInfo deviceInfo;
+
+ private TextView deviceNameView;
+ private TextView deviceRssiView;
+ private LinearLayout gaugesLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_display_control);
+
+ final Intent intent = getIntent();
+ String deviceAddress = intent.getStringExtra(IntentExtra.DEVICE_ADDRESS.name());
+ deviceInfo = new DeviceInfo(deviceAddress, 0);
+ deviceInfo.name = intent.getStringExtra(IntentExtra.DEVICE_NAME.name());
+
+ Intent displayServiceIntent = new Intent(this, DefaultDisplayService.class);
+ bindService(displayServiceIntent, serviceConnection, BIND_AUTO_CREATE);
+
+ deviceNameView = (TextView) findViewById(R.id.device_name);
+ deviceRssiView = (TextView) findViewById(R.id.device_rssi);
+ gaugesLayout = (LinearLayout) findViewById(R.id.gauges);
+
+ Button disconnectButton = (Button) findViewById(R.id.button_disconnect);
+ disconnectButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ disconnect();
+ }
+ });
+
+ updateValues();
+ }
+
+ private void updateValues() {
+ deviceNameView.setText(deviceInfo.name != null ? deviceInfo.name : getText(R.string.name_unknown));
+ deviceRssiView.setText(getText(R.string.rssi) + ": " + (deviceInfo.rssi != 0 ? valueOf(deviceInfo.rssi) : ""));
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ registerReceiver(displayServiceBroadcastReceiver, IntentAction.ALL_FILTER);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(displayServiceBroadcastReceiver);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ displayService.disconnect();
+ unbindService(serviceConnection);
+ displayService = null;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_display_control, menu);
+ return true;
+ }
+
+ public void disconnect() {
+ if (displayService != null) {
+ displayService.disconnect();
+ }
+
+ finish();
+ }
+
+ private final ServiceConnection serviceConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
+ displayService = ((LocalBinder) service).getService();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ displayService = null;
+ }
+ };
+
+ private final BroadcastReceiver displayServiceBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+
+ IntentAction action = IntentAction.valueOf(intent);
+
+ String deviceAddress = intent.getStringExtra(IntentExtra.DEVICE_ADDRESS.name());
+
+ if (action == IntentAction.DEVICE_UPDATE && deviceInfo.address.equals(deviceAddress)) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ deviceInfo.update(intent);
+
+ if (intent.hasExtra(IntentExtra.CONNECTED.name())) {
+ boolean connected = intent.getBooleanExtra(IntentExtra.CONNECTED.name(), false);
+
+ if (!connected) {
+ finish();
+ }
+ }
+ updateValues();
+ }
+ });
+ }
+ }
+ };
+}
diff --git a/app/src/main/java/no/topi/fiken/display/DisplayService.java b/app/src/main/java/no/topi/fiken/display/DisplayService.java
new file mode 100644
index 0000000..c907138
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/DisplayService.java
@@ -0,0 +1,102 @@
+package no.topi.fiken.display;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+
+public interface DisplayService {
+
+ enum IntentExtra {
+ DEVICE_NAME,
+ DEVICE_ADDRESS,
+ DEVICE_IS_DISPLAY,
+ RSSI,
+ SCANNING,
+ CONNECTED,
+ }
+
+ public static enum IntentAction {
+ DEVICE_UPDATE;
+
+ private final String key = getClass().getName() + "." + name();
+
+ public Intent intent() {
+ return new Intent(key);
+ }
+
+ public static final IntentFilter ALL_FILTER = new IntentFilter() {{
+ for (DefaultDisplayService.IntentAction intentAction : DefaultDisplayService.IntentAction.values()) {
+ addAction(intentAction.key);
+ }
+ }};
+
+ public static IntentAction valueOf(Intent intent) {
+ try {
+ return valueOf(intent.getAction().replaceAll(".*\\.", ""));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+ }
+
+ public class LocalBinder extends Binder {
+ private final DisplayService service;
+
+ public LocalBinder(DisplayService service) {
+ this.service = service;
+ }
+
+ DisplayService getService() {
+ return service;
+ }
+ }
+
+ boolean initialize();
+
+ void startScan();
+
+ void stopScan();
+
+ boolean connect(String address);
+
+ public void disconnect();
+
+ class DeviceInfo {
+ final String address;
+ int rssi = 0;
+ Boolean isDisplay = null;
+ String name;
+
+ DeviceInfo(String address, int rssi) {
+ this.address = address;
+ this.rssi = rssi;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DeviceInfo that = (DeviceInfo) o;
+
+ return address.equals(that.address);
+ }
+
+ @Override
+ public int hashCode() {
+ return address.hashCode();
+ }
+
+ public void update(Intent intent) {
+ if (intent.hasExtra(IntentExtra.DEVICE_IS_DISPLAY.name())) {
+ isDisplay = intent.getBooleanExtra(IntentExtra.DEVICE_IS_DISPLAY.name(), false);
+ }
+ if (intent.hasExtra(IntentExtra.RSSI.name())) {
+ rssi = intent.getIntExtra(IntentExtra.RSSI.name(), 0);
+ }
+ if (intent.hasExtra(IntentExtra.DEVICE_NAME.name())) {
+ name = intent.getStringExtra(IntentExtra.DEVICE_NAME.name());
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/no/topi/fiken/display/ExceptionHandler.java b/app/src/main/java/no/topi/fiken/display/ExceptionHandler.java
new file mode 100644
index 0000000..3d4560b
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/ExceptionHandler.java
@@ -0,0 +1,22 @@
+package no.topi.fiken.display;
+
+import android.util.Log;
+
+public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
+ private final static String TAG = DefaultDisplayService.class.getSimpleName();
+
+ public static final ExceptionHandler EXCEPTION_HANDLER = new ExceptionHandler();
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ Log.e(TAG, "Uncaught", ex);
+
+ if (ex instanceof RuntimeException) {
+ throw (RuntimeException) ex;
+ }
+ if (ex instanceof Error) {
+ throw (Error) ex;
+ }
+ throw new RuntimeException(ex);
+ }
+}
diff --git a/app/src/main/java/no/topi/fiken/display/MainActivity.java b/app/src/main/java/no/topi/fiken/display/MainActivity.java
new file mode 100644
index 0000000..d9dc073
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/MainActivity.java
@@ -0,0 +1,335 @@
+package no.topi.fiken.display;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.ListActivity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.lang.String.valueOf;
+import static no.topi.fiken.display.DisplayService.IntentAction;
+import static no.topi.fiken.display.DisplayService.IntentExtra;
+import static no.topi.fiken.display.ExceptionHandler.EXCEPTION_HANDLER;
+
+public class MainActivity extends ListActivity {
+ private final static String TAG = MainActivity.class.getSimpleName();
+
+ // Stops scanning after 10 seconds.
+ private static final long SCAN_PERIOD = 3 * 1000;
+
+ private static final int REQUEST_ENABLE_BT = 1;
+
+ private DisplayListAdapter displayList;
+ private Handler handler;
+ private BluetoothAdapter mBluetoothAdapter;
+ private boolean mScanning;
+ private DisplayService displayService;
+ private String deviceToShow;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log.i(TAG, "onCreate");
+ Thread.setDefaultUncaughtExceptionHandler(EXCEPTION_HANDLER);
+ super.onCreate(savedInstanceState);
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.title_devices);
+ }
+ handler = new Handler();
+
+ // Use this check to determine whether BLE is supported on the device. Then you can
+ // selectively disable BLE-related features.
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
+ Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
+ // BluetoothAdapter through BluetoothManager.
+ final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ mBluetoothAdapter = bluetoothManager.getAdapter();
+
+ // Checks if Bluetooth is supported on the device.
+ if (mBluetoothAdapter == null) {
+ Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ ServiceConnection serviceConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
+ displayService = ((DisplayService.LocalBinder) service).getService();
+ if (!displayService.initialize()) {
+ finish();
+ }
+
+ startScan();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ stopScan();
+ displayService = null;
+ }
+ };
+
+ Intent displayServiceIntent = new Intent(this, DefaultDisplayService.class);
+ bindService(displayServiceIntent, serviceConnection, BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onResume() {
+ Log.i(TAG, "onResume");
+
+ super.onResume();
+
+ // Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
+ // fire an intent to display a dialog asking the user to grant permission to enable it.
+ if (!mBluetoothAdapter.isEnabled()) {
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+ }
+
+ registerReceiver(displayServiceBroadcastReceiver, IntentAction.ALL_FILTER);
+ }
+
+ @Override
+ protected void onPause() {
+ Log.i(TAG, "onPause");
+
+ super.onPause();
+ stopScan();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.i(TAG, "onActivityResult");
+
+ // User chose not to enable Bluetooth.
+ if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
+ finish();
+ return;
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ Log.i(TAG, "onCreateOptionsMenu");
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+
+ if (!mScanning) {
+ menu.findItem(R.id.menu_stop).setVisible(false);
+ menu.findItem(R.id.menu_scan).setVisible(true);
+ menu.findItem(R.id.menu_refresh).setActionView(null);
+ } else {
+ menu.findItem(R.id.menu_stop).setVisible(true);
+ menu.findItem(R.id.menu_scan).setVisible(false);
+ menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_indeterminate_progress);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ Log.i(TAG, "onOptionsItemSelected");
+
+ switch (item.getItemId()) {
+ case R.id.menu_scan:
+ startScan();
+ break;
+ case R.id.menu_stop:
+ stopScan();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void startScan() {
+ displayList = new DisplayListAdapter();
+ setListAdapter(displayList);
+
+ displayService.startScan();
+
+ // Stops scanning after a pre-defined scan period.
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ displayService.stopScan();
+ invalidateOptionsMenu();
+ }
+ }, SCAN_PERIOD);
+ }
+
+ private void stopScan() {
+ if (displayService != null) {
+ displayService.stopScan();
+ }
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ stopScan();
+
+ DisplayService.DeviceInfo state = displayList.getDevice(position);
+
+ if (!displayService.connect(state.address)) {
+ Toast.makeText(this, "Could not connect to " + state.address, Toast.LENGTH_SHORT).show();
+ } else {
+ deviceToShow = state.address;
+ }
+ }
+
+ private final BroadcastReceiver displayServiceBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ IntentAction action = IntentAction.valueOf(intent);
+
+ String deviceAddress = intent.getStringExtra(IntentExtra.DEVICE_ADDRESS.name());
+
+ if (action == IntentAction.DEVICE_UPDATE && deviceAddress != null) {
+ DisplayService.DeviceInfo device = displayList.getDevice(deviceAddress, true);
+
+ device.update(intent);
+
+ if (intent.hasExtra(IntentExtra.CONNECTED.name())) {
+ boolean connected = intent.getBooleanExtra(IntentExtra.CONNECTED.name(), false);
+
+ if (connected) {
+ if (deviceToShow != null && deviceToShow.equals(device.address)) {
+ Log.i(TAG, "connected to " + deviceToShow);
+ final Intent intent = new Intent(context, DisplayControlActivity.class);
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), device.address);
+ intent.putExtra(IntentExtra.DEVICE_NAME.name(), device.name);
+ startActivity(intent);
+ }
+ }
+ }
+ displayList.notifyDataSetChanged();
+ }
+
+ if (intent.hasExtra(IntentExtra.SCANNING.name())) {
+ mScanning = intent.getBooleanExtra(IntentExtra.SCANNING.name(), false);
+ invalidateOptionsMenu();
+ }
+ }
+ });
+ }
+ };
+
+ static class ViewHolder {
+ final TextView deviceName;
+ final TextView deviceAddress;
+ final TextView rssi;
+ final TextView isDisplay;
+
+ ViewHolder(TextView deviceName, TextView deviceAddress, TextView rssi, TextView isDisplay) {
+ this.deviceName = deviceName;
+ this.deviceAddress = deviceAddress;
+ this.rssi = rssi;
+ this.isDisplay = isDisplay;
+ }
+ }
+
+ private class DisplayListAdapter extends BaseAdapter {
+ private List<DisplayService.DeviceInfo> devices = new ArrayList<DisplayService.DeviceInfo>();
+ private LayoutInflater inflater = MainActivity.this.getLayoutInflater();
+
+ public DisplayService.DeviceInfo getDevice(int position) {
+ return devices.get(position);
+ }
+
+ public DisplayService.DeviceInfo getDevice(String address, boolean create) {
+ for (DisplayService.DeviceInfo device : devices) {
+ if (device.address.equals(address)) {
+ return device;
+ }
+ }
+
+ DisplayService.DeviceInfo deviceInfo = null;
+ if (create) {
+ deviceInfo = new DisplayService.DeviceInfo(address, 0);
+ devices.add(deviceInfo);
+ }
+ return deviceInfo;
+ }
+
+ @Override
+ public int getCount() {
+ return devices.size();
+ }
+
+ @Override
+ public Object getItem(int i) {
+ return devices.get(i);
+ }
+
+ @Override
+ public long getItemId(int i) {
+ return i;
+ }
+
+ @Override
+ public View getView(int i, View view, ViewGroup viewGroup) {
+ ViewHolder viewHolder;
+
+ if (view == null) {
+ view = inflater.inflate(R.layout.listitem_device, null);
+ viewHolder = new ViewHolder(
+ (TextView) view.findViewById(R.id.device_name),
+ (TextView) view.findViewById(R.id.device_address),
+ (TextView) view.findViewById(R.id.device_rssi),
+ (TextView) view.findViewById(R.id.device_isDisplay));
+ view.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) view.getTag();
+ }
+
+ DisplayService.DeviceInfo state = devices.get(i);
+ if (state.name != null && state.name.length() > 0) {
+ viewHolder.deviceName.setText(state.name);
+ } else {
+ viewHolder.deviceName.setText(R.string.unknown_device);
+ }
+ viewHolder.deviceAddress.setText(state.address);
+
+ viewHolder.rssi.setText(getText(R.string.rssi) + ": " +
+ (state.rssi != 0 ? valueOf(state.rssi) : getText(R.string.rssi_unknown)));
+
+ viewHolder.isDisplay.setText("Is display: " +
+ (state.isDisplay != null ? state.isDisplay : "unknown"));
+ view.setClickable(state.isDisplay != null && state.isDisplay);
+
+ return view;
+ }
+ }
+}